Перейти к содержанию
Hardtmuth

[SOC] Мелкие правки движка

Рекомендуемые сообщения

 

[SOC] Мелкие правки движка

Ковыряемся в исходниках SoC. На данный момент тема ориентирована в большей степени на новичков. Здесь будут собираться готовые решения по мелкому ковырянию исходников движка SoC. Собственно приглашаю всех желающих присоединиться.

P.S. обсуждение также приветствуется.

Спойлер

Колбек - это функция, которая вызывается при определенном событии, например нажатие клавиши, выстрел из оружия, смерть актора и т.д.

Создать такой колбек не сложно. Рассмотрим на примере создания колбеков на удары ножом на ЛКМ и ПКМ. Никаких аргументов здесь передаваться не будет, просто вызов функции при ударе. Итак.

1. В файле WeaponKnife.cpp добавим в начало файла после всех инклудов ещё четыре, необходимые для колбеков актора:


#include "pch_script.h"
#include "script_callback_ex.h"
#include "script_game_object.h"
#include "alife_object_registry.h"

2. Далее ищем: void CWeaponKnife::switch2_Attacking (u32 state) и после:


m_pHUD->animPlay(random_anim(mhud_attack),        FALSE, this, state);

пишем:


g_actor->callback(GameObject::eKnifeAttackOne)();

Аналогично после:


m_pHUD->animPlay(random_anim(mhud_attack2),        FALSE, this, state);

пишем:


g_actor->callback(GameObject::eKnifeAttackTwo)();

3. Далее наши колбеки нужно зарегистрировать:
- в файле: game_object_space.h в список колбеков по аналогии в конец добавить:


eKnifeAttackOne,
eKnifeAttackTwo,

И наконец, в: script_game_object_script.cpp по аналогии с другими колбеками добавляем опять же в конец:


value("knife_attack_one", int(GameObject::eKnifeAttackOne) ),
value("knife_attack_two", int(GameObject::eKnifeAttackTwo) )

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

В функцию:


function actor_binder:net_destroy()

добавляем:


function actor_binder:reinit()

добавить:


self.object:set_callback(callback.knife_attack_one, self.knife_attack_one, self)
self.object:set_callback(callback.knife_attack_two, self.knife_attack_two, self)

И добавить функции, которые и будут вызываться при срабатывании колбека:


function actor_binder:knife_attack_one()
news_manager.send_tip(db.actor, "Удар ЛКМ") --сообщение для теста
end

function actor_binder:knife_attack_two()
news_manager.send_tip(db.actor, "Удар ПКМ") --сообщение для теста
end

 


Спойлер

Я думаю, многие сталкивались с функцией iterate_inventory, которая перебирает весь инвентарь и для каждого вызывает функцию. Если нам нужно перебрать все предметы на поясе, чтобы проверить, есть там какой-либо предмет или нет, можно, конечно, перебрать весь инвентарь и проверять для каждого предмета на поясе ли он или нет. Но можно создать отдельную функцию, которая будет перебирать предметы только на поясе.

1. В файле: script_game_object_script3.cpp ищем iterate_inventory и по аналогии делаем вариант для пояса:


.def("iterate_inventory",        &CScriptGameObject::IterateInventory)
.def("iterate_belt",             &CScriptGameObject::IterateBelt)

Теперь в script_game_object.h зарегистрируем нашу функцию:


void         IterateInventory    (luabind::functor<void> functor, luabind::object object);
void         IterateBelt     (luabind::functor<void> functor, luabind::object object);

И, наконец, в файле: script_game_object_inventory_owner.cpp добавляем саму функцию.

Найдите функцию для инвентаря, сделайте также.


void CScriptGameObject::IterateBelt    (luabind::functor<void> functor, luabind::object object)
{
    CInventoryOwner            *inventory_owner = smart_cast<CInventoryOwner*>(&this->object());
    if (!inventory_owner) {
        ai().script_engine().script_log        (ScriptStorage::eLuaMessageTypeError,"CScriptGameObject::IterateBelt non-CInventoryOwner object !!!");
        return;
    }

    TIItemContainer::iterator    I = inventory_owner->inventory().m_belt.begin();
    TIItemContainer::iterator    E = inventory_owner->inventory().m_belt.end();
    for ( ; I != E; ++I)
        functor                (object,(*I)->object().lua_game_object());
}

Как видите, разница тут лишь в том, что предметы перебираются не все, а только те, которые на поясе: так как вместо m_all мы используем m_belt.

Как использовать в скриптах:
Используется как и функция iterate_inventory


Спойлер

1) UIDiaryWnd2.cpp:


void CUIDiaryWnd::LoadJournalTab (ARTICLE_DATA::EArticleType _type)
{
delete_data (m_ArticlesDB);

m_UILeftWnd->AttachChild (m_SrcListWnd);
m_SrcListWnd->Show (true);

m_UIRightWnd->AttachChild (m_DescrView);
m_DescrView->Show (true);

if(Actor()->encyclopedia_registry->registry().objects_ptr())
{
ARTICLE_VECTOR::const_iterator it = Actor()->encyclopedia_registry->registry().objects_ptr()->begin();
for(; it != Actor()->encyclopedia_registry->registry().objects_ptr()->end(); it++)
{
if (_type == it->article_type)

{
// Исправление отображения зеленым цветом прочитанных записей в дневнике КПК
AddDiaryArticle(it->article_id, it->readed);

}
}
}
g_pda_info_state &= !pda_section::journal;

}

void CUIDiaryWnd::OnSrcListItemClicked (CUIWindow* w,void* p)
{
CUITreeViewItem* pSelItem = (CUITreeViewItem*)p;
m_DescrView->Clear ();
if (!pSelItem->IsRoot())
{
CUIEncyclopediaArticleWnd* article_info = xr_new<CUIEncyclopediaArticleWnd>();
article_info->Init ("encyclopedia_item.xml","encyclopedia_wnd:objective_item");
article_info->SetArticle (m_ArticlesDB[pSelItem->GetValue()]);
m_DescrView->AddWindow (article_info, true);

// Исправление отображения зеленым цветом прочитанных записей в дневнике КПК
if (!pSelItem->IsArticleReaded())
{
if(Actor()->encyclopedia_registry->registry().objects_ptr())
{
for(ARTICLE_VECTOR::iterator it = Actor()->encyclopedia_registry->registry().objects().begin();
it != Actor()->encyclopedia_registry->registry().objects().end(); it++)
{
if (ARTICLE_DATA::eJournalArticle == it->article_type &&
m_ArticlesDB[pSelItem->GetValue()]->Id() == it->article_id)
{
it->readed = true;
break;
}
}
}
}
}
}

В конце UIDiaryWnd2.cpp дописать:

// Исправление отображения зеленым цветом прочитанных записей в дневнике КПК
void CUIDiaryWnd::AddDiaryArticle(shared_str article_id, bool bReaded)
{
m_ArticlesDB.resize(m_ArticlesDB.size() + 1);
CEncyclopediaArticle*& a = m_ArticlesDB.back();
a = xr_new<CEncyclopediaArticle>();
a->Load(article_id);

CreateTreeBranch(a->data()->group, a->data()->name, m_SrcListWnd, m_ArticlesDB.size()-1,
m_pTreeRootFont, m_uTreeRootColor, m_pTreeItemFont, m_uTreeItemColor, bReaded);
}

2) UIDiaryWnd.h:


void UnloadNewsTab ();
void LoadNewsTab ();
void Reload (EDiaryFilter new_filter);
// Исправление отображения зеленым цветом прочитанных записей в дневнике КПК
void AddDiaryArticle (shared_str, bool bReaded);

 


Спойлер

Я использую ревизию с XP DEV, там уже проведена работа над слотами. Если кто-то использует другую версию исходников, могут быть различия.

Создадим слот для второго детектора:
В UIInventoryWnd.cpp после:


m_pUISlotQuickAccessList_3   = xr_new<CUIDragDropListEx>(); AttachChild(m_pUISlotQuickAccessList_3); m_pUISlotQuickAccessList_3->SetAutoDelete(true);
xml_init.InitDragDropListEx   (uiXml, "dragdrop_slot_quick_access_3", 0, m_pUISlotQuickAccessList_3);
BindDragDropListEnents    (m_pUISlotQuickAccessList_3);

добавим:


m_pUIDetAdvList      = xr_new<CUIDragDropListEx>(); AttachChild(m_pUIDetAdvList); m_pUIDetAdvList->SetAutoDelete(true);
xml_init.InitDragDropListEx   (uiXml, "dragdrop_slot_det_adv", 0, m_pUIDetAdvList);
BindDragDropListEnents    (m_pUIDetAdvList);

после:


m_slots_array[SLOT_QUICK_ACCESS_3]  = m_pUISlotQuickAccessList_3;

добавить:


m_slots_array[DET_ADV_SLOT]          = m_pUIDetAdvList;

В UIInventoryWnd.h после:


CUIDragDropListEx*   m_pUISlotQuickAccessList_3;

добавить:


CUIDragDropListEx* m_pUIDetAdvList;

В файле: uiinventorywnd2.cpp сразу после:


_itm            = m_pInv->m_slots[SLOT_QUICK_ACCESS_3].m_pIItem;
if(_itm)
{
    CUICellItem* itm    = create_cell_item(_itm);
    m_pUISlotQuickAccessList_3->SetItem  (itm);
}

добавим:


_itm               = m_pInv->m_slots[DET_ADV_SLOT].m_pIItem;
if(_itm)
{
    CUICellItem* itm    = create_cell_item(_itm);
    m_pUIDetAdvList->SetItem  (itm);
}

после:


m_pUISlotQuickAccessList_3->ClearAll    (true);

добавим:


m_pUIDetAdvList->ClearAll    (true);

В файле UIInventoryWnd3.cpp после:


case INVENTORY_TO_SLOT15_ACTION:
    CurrentIItem()->SetSlot(SLOT_QUICK_ACCESS_3);
    break;

добавим:


case INVENTORY_TO_SLOT16_ACTION:
    CurrentIItem()->SetSlot(DET_ADV_SLOT);
    break;

В файле inventory_space.h после:


#define SLOT_QUICK_ACCESS_3 15

добавить:


#define DET_ADV_SLOT        16

Также вносим изменения в slots_total. Число должно быть на один больше последнего номера слотов. В нашем случае будет вот так:


#define SLOTS_TOTAL     17

В файле UIMessages.h после:


INVENTORY_TO_SLOT15_ACTION,

пишем:


INVENTORY_TO_SLOT16_ACTION,

Осталось только зарегистрировать наш новый слот в конфигах игры по аналогии с остальными:

В inventory_new.xml и в inventory_new_16:


<dragdrop_slot_det_adv x="581" y="673" width="100" height="50" cell_width = "48" cell_height="50" rows_num="1" cols_num="2" custom_placement="0" show_grid = "0"/>

И в system.ltx:


slot_persistent_1  = false  ;нож 0
slot_persistent_2  = false  ;пистолет 1
slot_persistent_3  = false  ;автомвт 2
slot_persistent_4  = true  ;гранаты 3
slot_persistent_5  = false  ;бинокль 4
slot_persistent_6  = true  ;болт 5
slot_persistent_7  = false  ;костюм 6
slot_persistent_8  = false  ;пда 7
slot_persistent_9  = false  ;детектор 8
slot_persistent_10  = false  ;фонарь 9
slot_persistent_11  = true  ;артефакт 10
slot_persistent_12  = false  ;шлем 11
slot_persistent_13  = false  ;яч1 12
slot_persistent_14  = false  ;яч2 13
slot_persistent_15  = false  ;яч3 14
slot_persistent_16  = false  ;яч4 15
slot_persistent_17  = false  ;детектор 2 16

 


Спойлер

В CoP некоторые модификации броников увеличивают скорость восстановления здоровья, энергии и т.д. В оригинале ТЧ есть пара уникальных костюмов, в которых даже прописан параметр health_restore_speed, но конечно же, он не работает. Добавить эти параметры очень просто:

В CustomOutfit.cpp после:


m_HitTypeProtection[ALife::eHitTypePhysicStrike]= READ_IF_EXISTS(pSettings, r_float, section, "physic_strike_protection", 0.0f);

Добавим:


m_fHealthRestoreSpeed  = READ_IF_EXISTS(pSettings, r_float,    section, "health_restore_speed",    0.0f );
m_fRadiationRestoreSpeed    = READ_IF_EXISTS(pSettings, r_float,    section, "radiation_restore_speed", 0.0f );
m_fSatietyRestoreSpeed  = READ_IF_EXISTS(pSettings, r_float,    section, "satiety_restore_speed",   0.0f );
m_fPowerRestoreSpeed  = READ_IF_EXISTS(pSettings, r_float,    section, "power_restore_speed",     0.0f );
m_fBleedingRestoreSpeed  = READ_IF_EXISTS(pSettings, r_float,    section, "bleeding_restore_speed",  0.0f );

Сразу хочу заметить, что если в конфиге будет отсутствовать один из новых параметров, его значение будет равным 0.0. Идем дальше.

В файле CustomOutfit.h после:


public:
    float       m_additional_weight;
    float       m_additional_weight2;

добавим:


float             m_fHealthRestoreSpeed;
float              m_fRadiationRestoreSpeed;
float              m_fSatietyRestoreSpeed;
float             m_fPowerRestoreSpeed;
float             m_fBleedingRestoreSpeed;

Теперь эти параметры читаются, но пока ещё никак не используются.

Для их использования в Actor.cpp после, например:


float    CActor::HitArtefactsOnBelt  (float hit_power, ALife::EHitType hit_type)
{
    float res_hit_power_k  = 1.0f;
    float _af_count    = 0.0f;
................................................................................
    res_hit_power_k   -= _af_count;
    return     res_hit_power_k * hit_power;
}

Добавим:


#define OUTFIT_UPDATE_TIME 0.100f

#include "CustomOutfit.h"

void CActor::UpdtateOutfitInSlot()
{
    static float update_time = 0;

    float f_update_time = 0;

    if(update_time<OUTFIT_UPDATE_TIME)
    {
  update_time += conditions().fdelta_time();
  return;
    }
    else
    {
  f_update_time    = update_time;
  update_time  = 0.0f;
    }

    CCustomOutfit* outfit = GetOutfit();
  if(outfit)
  {
   conditions().ChangeBleeding(outfit->m_fBleedingRestoreSpeed*f_update_time);
   conditions().ChangeHealth(outfit->m_fHealthRestoreSpeed*f_update_time);
   conditions().ChangePower(outfit->m_fPowerRestoreSpeed*f_update_time);
   conditions().ChangeSatiety(outfit->m_fSatietyRestoreSpeed*f_update_time);
#ifndef OBJECTS_RADIOACTIVE // alpet: отключается для избежания двойного хита
   conditions().ChangeRadiation  (outfit->m_fRadiationRestoreSpeed*f_update_time);
#endif
  }

}

Теперь после:


//для свойств артефактов, находящихся на поясе
UpdateArtefactsOnBelt      ();

добавим:


UpdtateOutfitInSlot          ();

Теперь в Actor.h после:


//свойства артефактов
virtual void  UpdateArtefactsOnBelt    ();
virtual void  MoveArtefactBelt  (const CArtefact* artefact, bool on_belt);
virtual float  HitArtefactsOnBelt  (float hit_power, ALife::EHitType hit_type);
const xr_vector<const CArtefact*>& ArtefactsOnBelt() {return m_ArtefactsOnBelt;}

добавим:


//свойства брони
virtual void  UpdtateOutfitInSlot    ();

 


Спойлер

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

В файле: ActorInput.cpp после:


#include "../../build_config_defines.h"

добавим:


#include "pch_script.h"
#include "InventoryOwner.h"
#include "script_game_object.h"
#include "script_game_object_impl.h"

На самом деле я точно не уверен, что нужны все четыре инклуда, возможно в этом файле я прописывал инклуд для чего-то ещё. Но на всякий случай добавьте все четыре.

Также, например после:


case kCROUCH_TOGGLE:
  {
   g_bAutoClearCrouch = !g_bAutoClearCrouch;
   if (!g_bAutoClearCrouch)
    mstate_wishful |= mcCrouch;

  }break;

Добавим:


case kCLOCK:
  {
  luabind::functor<void>    clock_key;
  if (ai().script_engine().functor("gz_items_hud.clock_key",clock_key))
  clock_key();
    }break;

Как видите у меня здесь кнопка показа часов. "gz_items_hud.clock_key" это как раз скрипт и функция, которые будут вызваны при нажатии кнопки.

Теперь в файле key_binding_registrator_script.cpp в:


class_<enum_exporter<EGameActions> >("key_bindings")
   .enum_("commands")
   [

по аналогии с остальным добавим:


value("kCLOCK",            int(kCLOCK)),

Если будете добавлять в самый конец, не забудьте убрать запятую после двойной скобки.

Потом в файле: xr_level_controller.cpp добавим по аналогии с остальными:


{ "clock",          kCLOCK          ,_sp},

Как я понял, _sp - работает в одиночной игре, _both в одиночной и мультиплеере, _mp - только в мультиплеере. Но это нужно проверять. Я мультиплеер не использую, стоит _sp и пускай себе стоит.

Теперь в xr_level_controller.h добавим:


kCLOCK,

к другим кнопкам.

Кстати таким образом, если это необходимо, можно заменить какое-то движковое действие на скриптовое. Просто ищем case kНУЖНАЯ КНОПКА: и меняем "начинку" на свою.

Всё, в движке кнопка имеется, переходим к конфигам:

Файл u\ui_keybinding.xml добавим в нужную группу кнопок:


<command id="kb_clock"            exe="clock"/>

Теперь в test\rus\ui_st_keybinding.xml:


<string id="kb_clock">
<text>Часы</text>
</string>

Если вы хотите чтобы по умолчанию кнопка была уже установлена, ищем файл default_controls.ltx в папке config оттуда берутся данные о том, какую кнопку на какое действие назначить при создании игрой файла user.ltx

Просто пишем:


bind clock kC

Функция в скрипте самая обычная:


--//*** ЧАСЫ НА РУКЕ
function clock_key() -- вызывается из движка при нажатии кнопки часов
--действия при нажатии кнопки
end

На этом всё. Правда я не проверял, что будет, если скрипта или функции не существует.


Спойлер

Sourcehttps://yadi.sk/d/LYCNOX-buFd7a

Добавлены параметры для аномалий:


; это настройки онлайн спавна

spawn_blowout_artefacts = on ; главный переключатель возможности рождения
artefact_spawn_probability = 0.05 ;вероятность, что во время срабатывания аномалии будет рожден артефакт

; добавленные мной
birth_on_death_probability = 0.2 ; вероятность рождения артефакта после смерти существа в аномалии
birth_on_torn_probability = 0.5 ; вероятность рождения артефакта при разрыве тела в мясорубке

birth_on_nonalive = true ; возможность рождения при срабатывании на предмет
birth_on_alive = true ; возможность рождения при срабатывании на живое существо
birth_on_dead = true ; возможность рождения при срабатывании на труп

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 

Есть у вас исходники движка? 
Поделитесь, кто нибудь, пожалуйста
upd: вроде как нашёл

Изменено пользователем Gowther

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 

А можно статью или ссылку на то где будет рассказываться работа с движком от начала и до конца, то есть раскажеться что где скачать, как редактировать, как сохранить, распоковать/запоковать и прочие ньюансы, не понятные новичкам по редактированию движка 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
 
В 19.06.2021 в 13:06, Alex Kireev сказал:

А можно статью или ссылку на то где будет рассказываться работа с движком от начала и до конца, то есть раскажеться что где скачать, как редактировать, как сохранить, распоковать/запоковать и прочие ньюансы, не понятные новичкам по редактированию движка 

Без знаний C++ или похожих на него движков в дословном гайде смысла нет, иначе автору придется делать все за этих новичков. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти

  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу