Kondr48 314 Опубликовано 15 Августа 2016 (изменено) Ковыряемся в исходниках SoC. На данный момент тема ориентирована в большей степени на новичков. Здесь будут собираться готовые решения по мелкому ковырянию исходников движка SoC. Собственно приглашаю всех желающих присоединиться. Сборка исходников, необходимые программы. Расширения для диалогов: Скрытый текст Один из вариантов реализации случайных фраз у диалогов. Автор: Winsor. Часть 2. + немного переделанная функция дампа xml Автор: Winsor. Исправление ошибок: Скрытый текст Исправление отображения зеленым цветом прочитанных записей в разделе КПК "Дневник". Автор: lvg_brest Маленькое исправление колбека before save. Нашел: Карлан. Исправление сохранения клиентских объектов. Автор: Shoker Фикс вертикальной синхронизации (r2) Автор: SkyLoader Фикс не отключения ПНВ при удалении броника ф-циями release\transfer_item или продаже\выкладывании в другой инвентарь, автор: UnLoaded Исправление вылета по отсутствию звука. Путь до звука выводится в лог (как с текстурами в ЗП). Исправление пулестойкости костюма. Расширение скриптового функционала: Скрытый текст Функции, возвращающие текущий год и месяц. Функция перемотки времени из CoP. Создание колбеков для Актора на примере колбека на удар ножом. Назначение скриптам горячих клавиш, изменяемых в главном меню. Создание функции, перебирающей предметы на поясе. Работа с интерфейсом (худ, инвентарь и т.п.): визуальная часть: Скрытый текст Редактирование загрузочного экрана. Создание нового индикатора на худе. Расширение геймплея: Скрытый текст Создание нового слота. Новые параметры для бронежилетов. Восстановление функции рождения артефактов при срабатывании аномалии. Автор: Bak Добавление изменения переносимого веса в свойства артефактов. Массовое перемещение предметов между окнами, Автор: UnLoaded Зум в один клик. Автор: Shkiper2012. Дополнение: Kondr48 Порция мелких правок от Shkiper2012. Работа с рендером: Скрытый текст Скриншоты в формате png. Изменено 2 Ноября 2017 пользователем Kondr48 4 13 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 15 Августа 2016 (изменено) Создание колбеков для Актора на примере колбека на удар ножом Колбек - это функция, которая вызывается при определенном событии, например нажатие клавиши, выстрел из оружия, смерть актора и т.д. Создать такой колбек не сложно. Рассмотрим на примере создания колбеков на удары ножом на ЛКМ и ПКМ. Никаких аргументов здесь передаваться не будет, просто вызов функции при ударе. Итак. 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)(); должно получиться: Скрытый текст void CWeaponKnife::switch2_Attacking (u32 state) { if(m_bPending) return; if(state==eFire) { m_pHUD->animPlay(random_anim(mhud_attack), FALSE, this, state); g_actor->callback(GameObject::eKnifeAttackOne)(); } else { m_pHUD->animPlay(random_anim(mhud_attack2), FALSE, this, state); g_actor->callback(GameObject::eKnifeAttackTwo)(); } m_attackStart = true; m_bPending = true; } 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 Скрытый текст В функцию: function actor_binder:net_destroy() добавляем: self.object:set_callback(callback.knife_attack_one, nil) self.object:set_callback(callback.knife_attack_two, nil) В функцию 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 db.actor:iterate_belt(clear_belt, db.actor) --вызываем в нужном месте. clear_belt - функция, которая вызовется для каждого предмета на поясе. function clear_belt(npc, item) db.actor:move_to_ruck(item) --действие с предметом. Например перемещение в рюкзак. end Исправление отображения зеленым цветом прочитанных записей в разделе КПК "Дневник" by lvg_brest: Скрытый текст 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, Регистрация слота в конфигах игры: 1. В 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"/> 2. В 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 Изменено 22 Июня 2017 пользователем Kondr48 3 2 3 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 15 Августа 2016 (изменено) Новые параметры для бронежилетов. Скрытый текст В 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 На этом всё. Правда я не проверял, что будет, если скрипта или функции не существует. Изменено 22 Июня 2017 пользователем Kondr48 3 1 5 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 15 Августа 2016 (изменено) CRAZY_STALKER666, в исходниках вот здесь стоит посмотреть: FDemoRecord.cpp if (psHUD_Flags.test(HUD_DRAW)){ if ((Device.dwTimeGlobal/750)%3!=0) { // pApp->pFontSystem->SetSizeI (0.02f); pApp->pFontSystem->SetColor (color_rgba(255,0,0,255)); pApp->pFontSystem->SetAligment(CGameFont::alCenter); pApp->pFontSystem->OutSetI (0,-.05f); pApp->pFontSystem->OutNext ("%s","RECORDING"); pApp->pFontSystem->OutNext ("Key frames count: %d",iCount); pApp->pFontSystem->SetAligment(CGameFont::alLeft); pApp->pFontSystem->OutSetI (-0.2f,+.05f); pApp->pFontSystem->OutNext ("SPACE"); pApp->pFontSystem->OutNext ("BACK"); pApp->pFontSystem->OutNext ("ESC"); pApp->pFontSystem->OutNext ("F11"); pApp->pFontSystem->OutNext ("F12"); pApp->pFontSystem->SetAligment(CGameFont::alLeft); pApp->pFontSystem->OutSetI (0,+.05f); pApp->pFontSystem->OutNext ("= Append Key"); pApp->pFontSystem->OutNext ("= Cube Map"); pApp->pFontSystem->OutNext ("= Quit"); pApp->pFontSystem->OutNext ("= Level Map ScreenShot"); pApp->pFontSystem->OutNext ("= ScreenShot"); } } А в рамках 4 патча даже не знаю: хексом мб это искать и закомментировать. По сути это и смысл использования исходников: нашел то, что нужно и подправил/дописал. А так по сути тема больше для правок исходников. Изменено 15 Августа 2016 пользователем Kondr48 1 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 15 Августа 2016 (изменено) НаноБот, если колбек нужен будет не только актору, то да. Согласен. Однако в случае с ножом, есть ли в этом смысл, если этот колбек будет использоваться только для актора? P.S. Нет желания подкинуть сюда пару статеек по работе на исходниках? К примеру адаптацию Вашего проекта новая баллистика? Или те же колбеки? Изменено 22 Января 2017 пользователем Kondr48 1 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 29 Декабря 2016 (изменено) UnLoaded, Версия исходников 1.0007(rc1) Ну это еще не аргумент. Я например форкнулся с r180 с репозитория на xp-dev, собственно туторы по этой версии я и писал. Если у Вас именно "чистые" исходники, то для начала вам нужно добавить туда все недостоющие части кода по слотам из этой (или более высокой, если хотите) ревизии. А если ревизия итак оттуда, то, по данному "тутору" слоты легко создаются и m_slots_array там присутствует. Изменено 29 Декабря 2016 пользователем Kondr48 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 22 Января 2017 (изменено) Все мы с Вами знаем, что в ЗП добавлен ряд интересных и полезных индикаторов на худе. На самом деле добавить их легко. Разберем на примере индикатора поломки брони. Для начала, в движке CoP есть такой удобный инструмент как UIHelper, для дальнейшей работы он нам понадобится, поэтому добавляем в xrGame\ui: 1. UIHelper.h //////////////////////////////////////////////////////////////////////////// // Module : UIHelper.h // Created : 17.01.2008 // Author : Evgeniy Sokolov // Description : UI Helper class //////////////////////////////////////////////////////////////////////////// #ifndef UI_HELPER_H_INCLUDED #define UI_HELPER_H_INCLUDED class CUIXml; class CUIWindow; class CUIStatic; class UIHelper { public: UIHelper () {}; ~UIHelper () {}; static CUIStatic* CreateStatic ( CUIXml& xml, LPCSTR ui_path, CUIWindow* parent ); }; #endif // UI_HELPER_H_INCLUDED 2. UIHelper.cpp: //////////////////////////////////////////////////////////////////////////// // Module : UIHelper.cpp // Created : 17.01.2008 // Author : Evgeniy Sokolov // Description : UI Helper class implementation //////////////////////////////////////////////////////////////////////////// #include "StdAfx.h" #include "UIHelper.h" #include "UIXmlInit.h" CUIStatic* UIHelper::CreateStatic( CUIXml& xml, LPCSTR ui_path, CUIWindow* parent ) { CUIStatic* ui = new CUIStatic(); if(parent) { parent->AttachChild ( ui ); ui->SetAutoDelete ( true ); } CUIXmlInit::InitStatic ( xml, ui_path, 0, ui ); return ui; } Здесь я оставил только добавление статика, на самом деле в ЗП с помощью этого инструмента можно все что угодно добавлять. Переносите себе по мере необходимости , идем дальше. Нам нужен файл UIMainIngameWnd.h в группе protected: находим: CUIZoneMap* UIZoneMap; И после добавляем: CUIStatic* m_ind_outfit_broken; Теперь файл UIMainIngameWnd.cpp. Сначала не забудьте прописать #include "UIHelper.h" в начале файла там где все остальные. Находим: void CUIMainIngameWnd::Init() К примеру, после: m_UIIcons = xr_new<CUIScrollView>(); m_UIIcons->SetAutoDelete(true); xml_init.InitScrollView (uiXml, "icons_scroll_view", 0, m_UIIcons); AttachChild (m_UIIcons); Добавим: m_ind_outfit_broken = UIHelper::CreateStatic(uiXml, "indicator_outfit_broken", this); Затем находим: void CUIMainIngameWnd::Update() И, например, перед: // health&power UIHealthBar.SetProgressPos (m_pActor->GetfHealth()*100.0f); UIMotionIcon.SetPower (m_pActor->conditions().GetPower()*100.0f); Добавим: PIItem pItem = m_pActor->inventory().ItemFromSlot(OUTFIT_SLOT); m_ind_outfit_broken->Show(false); if (pItem) { float condition = pItem->GetCondition(); if (condition<0.75f) { m_ind_outfit_broken->Show(true); if(condition>0.5f) m_ind_outfit_broken->InitTexture("ui_inGame2_circle_Armorbroken_green"); else if(condition>0.25f) m_ind_outfit_broken->InitTexture("ui_inGame2_circle_Armorbroken_yellow"); else m_ind_outfit_broken->InitTexture("ui_inGame2_circle_Armorbroken_red"); } } Теперь разберем что нужно будет сделать в конфигах. indicator_outfit_broken это имя статика. Добавить надо его в maingame.xml вот так: <indicator_outfit_broken x="980" y="410" width="26" height="35" stretch="1"/> ну и, соответственно, "зарегистрировать" в игре вот эти текстуры: "ui_inGame2_circle_Armorbroken_green" "ui_inGame2_circle_Armorbroken_yellow" "ui_inGame2_circle_Armorbroken_red" На этом всё . Собственно у меня по такой схеме сделаны все подобные иконки, потому что система ТЧ показалась мне неоправданно сложной и громоздкой. Ну, быть может, это на мой дилетантский взгляд. P.S. Конечно, лучше совместить прогресс бар, который показывает состояние брони и этот индикатор. Чтобы два раза не дергать состояние костюма на апдейте. Но я не стал усложнять урок) Изменено 22 Января 2017 пользователем Kondr48 1 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 1 Февраля 2017 (изменено) Совсем мелочь, но мне понадобилось, мб ещё кому-то будет надо. В SHoC существует три функции, для возврата игрового времени, в пространстве level: get_time_days() -- день get_time_hours() -- час get_time_minutes() -- минута Собственно, для своих целей я добавил аналогичные функции, которые возвращают текущий месяц и год. level_script.cpp: Перед: u32 get_time_days() { u32 year = 0, month = 0, day = 0, hours = 0, mins = 0, secs = 0, milisecs = 0; split_time(Level().GetGameTime(), year, month, day, hours, mins, secs, milisecs); return day; } добавим: u32 get_time_year() { u32 year = 0, month = 0, day = 0, hours = 0, mins = 0, secs = 0, milisecs = 0; split_time(Level().GetGameTime(), year, month, day, hours, mins, secs, milisecs); return year; } u32 get_time_month() { u32 year = 0, month = 0, day = 0, hours = 0, mins = 0, secs = 0, milisecs = 0; split_time(Level().GetGameTime(), year, month, day, hours, mins, secs, milisecs); return month; } и перед: def("get_time_days", get_time_days), добавим: def("get_time_year", get_time_year), def("get_time_month", get_time_month), На этом - всё, использовать в своих скриптах, вот так: local year = level.get_time_year() local month = level.get_time_month() Изменено 1 Февраля 2017 пользователем Kondr48 1 1 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 2 Февраля 2017 (изменено) По сути с этого и нужно было начинать Сборка исходников в Visual Studio: Скрытый текст Это сложно назвать какой-то инструкцией, скорее описание того как это делаю я, на правильность не претендую, но у меня работает. Итак, для сборки исходников, нам нужно, для начала, их скачать. Я лично работаю на репозитории xp-dev, собственно альтернативы, которые в открытом доступе довольны печальны. Я лично "форкнулся" с r-180 потому что дальше там пошли какие то баги и непонятности, а так выбирать можно ревизию, собственно, любую, какую душе угодно. Дальнейшая статья будет относится именно к этому репо. Итак, скачать исходники можно тут. Для скачивания нам понадобится TorotoiseSVN. Найти можно на официальном сайте. Ссылка, которую нужно будет вставлять: https://xp-dev.com/svn/xray Как скачать исходники с помощью TorotoiseSVN Скрытый текст Создаём папку, в которую будем скачивать репозитории (для удобства лучше хранить все репозитории в одной папке, например, C:\SVN). Заходим в неё в проводнике Windows, нажимаем правой кнопкой мыши по свободному месту и выбираем «SVN Checkout…«. В поле «URL of repository» пишем полный URL до репозитория SVN, который нам нужно скачать, а в поле «Checkout directory» корректируем путь, в которой будет помещена локальная копия. Внимание! Вам нужно делать checkout только каталога /trunk/ svn-репозитория, либо корневого, если /trunk/ не существует (очень редко, т.к. в 99% репозиториев trunk существует и содержит всегда самую последнюю версию файлов). В списке «Checkout Depth» укажите «Fully recursive«, что означает, что будет скачан весь репозиторий от указанного пути. Если установить флажок «Omit externals«, то внешние файлы, на которые есть ссылки в репозитории (например, из других репозиториев, либо репозиториев третьих лиц), скачаны не будут, поэтому ставить флажок здесь не рекомендуется. В блоке «Revision» Вы можете запросить выдать Вам последнюю версию репозитория: «HEAD revision» (рекомендуется именно этот вариант), либо указанную Вам в поле ревизию: «Revision ###» (не рекомендуется). Кнопка «Show log» покажет Вам список изменений в репозитории с информацией об авторах каждого изменения, описании изменений (если авторы их вводили при коммите изменений), а также списке добавленных, изменённых и удалённых файлов. Здесь же можно просмотреть чем файл одной ревизии (версии) отличается от файла другой, а также запросить показать унифицированный diff-файл изменений либо нескольких файлов, либо нескольких ревизий (отображается только для текстовых файлов). Более подробную информацию о Log Viewer читайте ниже. Нажимаем кнопку «OK» и ждём скачивания репозитория с Subversion-сервера (зависит от скорости Вашего Интернет-соединения, а также от загруженности svn-сервера). Внимание! Некоторые svn-серверы могут запросить пароль. Если Вы не знаете пароля, то вводите anonsvn как логин и пароль. Это стандартный логин/пароль для анонимного доступа. Если Вы хотите в дальнейшем публиковать свои изменения в этот репозиторий и у Вас есть на это право, то введите здесь свой логин и пароль. Готово. В каталоге, который Вы указали на третьем шаге в поле «Checkout directory» теперь находится локальная версия репозитория (рабочая копия). Полная версия инструкции - здесь. Итак, теперь мы имеем на руках исходники. Для сборки нам понадобится установить на ПК: 1. Visual Studio 2010 (НЕ Express Version, у меня, по моему pro) 2. Visual Studio 2010 Service Pack 1 3. DirectX SDK June 2010. Я не уверен в необходимости его установки, но в изначальной инструкции он был. 4. DirectX SDK June 2007. Если ставили 2010, то 2007 ОБЯЗАТЕЛЬНО нужно ставить после 10. 5. Windows SDK 6. Скорее всего, понадобятся также некоторые dll из 6 патча: wrap_oal.dll, eax.dll, OpenAl32.dll (или же установить его). Для вашей звуковой карты, возможно придётся подобрать оптимальную комбинацию. У меня даже xrSound.dll какое-то время была от 1.0006. Как только мы установили всё это, открываем студию, ставим Solution Configurations - Release (насколько помню, дебаговая версия с репо не собирается) Все, дальше идет сборка, делать build\build solution я не советую, для этого проект, скорее всего придется отдельно настраивать. Я лично собираю библиотеки по очереди в таком порядке: 1. libogg_static 2. libtheora_static 3. libvorbis_static 4. libvorbisfile 5. nvtt 6. ode 7. xrD3D9-Null 8. LuaJIT 9. xrCore 10. Luabind 11. xrCDB 12. xrGameSpy 13. xrParticles 14. xrSound 15. xrXMLParser 16. xrNetServer 17. XR_3DA 18. xrCPU_Pipe 19. xrRender_R1 20. xrRender_R2 21. xrGame Скорее всего, можно и по другому, этот порядок у меня сложился эмпирически и вникать в настройки проекта мне пока банально лень: собирается и ладно. Собственно на этом всё. xrGame: level_script.cpp: В начала файла, перед: using namespace luabind; добавим инклуды: #include "alife_time_manager.h" #include "game_sv_single.h" #include "alife_simulator.h" Потом после: u32 get_time_minutes() { u32 year = 0, month = 0, day = 0, hours = 0, mins = 0, secs = 0, milisecs = 0; split_time(Level().GetGameTime(), year, month, day, hours, mins, secs, milisecs); return mins; } добавить: void change_game_time(u32 days, u32 hours, u32 mins) { game_sv_Single* tpGame = smart_cast<game_sv_Single *>(Level().Server->game); if (tpGame && ai().get_alife()) { u32 value = days * 86400 + hours * 3600 + mins * 60; float fValue = static_cast<float>(value); value *= 1000; // msec g_pGamePersistent->Environment().ChangeGameTime(fValue); tpGame->alife().time_manager().change_game_time(value); } } и в этом же файле, после: def("get_time_minutes", get_time_minutes), добавим: def("change_game_time", change_game_time), Теперь в файле alife_time_manager.h, после: IC float normal_time_factor () const; добавим: IC void change_game_time (u32 value); В файле alife_time_manager_inline.h добавим в конце: IC void CALifeTimeManager::change_game_time(u32 value) { m_game_time += value; } XR_3DA: Environment.h: после: shared_str GetWeather () { return CurrentWeatherName;} добавим: void ChangeGameTime (float game_time); Environment.cpp: перед: void CEnvironment::SetGameTime(float game_time, float time_factor) добавим: void CEnvironment::ChangeGameTime(float game_time) { fGameTime = NormalizeTime(fGameTime + game_time); }; Изменено 4 Апреля 2018 пользователем Kondr48 3 4 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 11 Февраля 2017 Итак, за те картинки и надписи, которые мы видим при загрузке локации / сохранения, отвечает файл x_ray.cpp. Не только за них, но всё, что мы там видим настраивается именно в нём. Собственно, интерес представляет: load_texture = "ui\\ui_load"; Всё однозначно - фон загрузочного экрана. strconcat (sizeof(temp),temp,"intro\\intro_",Levels[L].folder); тут формируется картинка загружаемой локации. А это: hLevelLogo.create ("font", "intro\\intro_no_start_picture"); текстура "белого шума" которая выводится, если нужной картинки для локации у нас нету. Это: pFontSystem->SetColor (color_rgba(35,71,74,255)); Цвет надписей типа: "Клиент: синхронизация". RGBA Это: pFontSystem->OutI (0.f,0.524f,app_title); Координаты надписи: x,y. Надпись отрисовывается по центру, о чём нам говорит: pFontSystem->SetAligment (CGameFont::alCenter); Теперь, как настраивать размер и положение картинки загружаемой локации: находим: //draw level-specific screenshot if(hLevelLogo){ смотрим дальше: r.lt.set (0,175); собственно, координаты, x и y. А это: r.rb.add (r.lt,Fvector2().set(1024,399)); уже размеры, длина ширина. В прогресс бар, честно, не вникал. Ну не нравится мне его реализация. У себя я его просто закомментировал. Но настройки можно посмотреть после: progress bar back_size.set (479,26); 1 1 2 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 16 Февраля 2017 (изменено) Кто-то из форумчан Карлан упоминал, вспомнил вот. Ошибка-опечатка в оригинальном репозитории xp-dev в колбеке на before save (вызывается перед созданием сейва, насколько я понял), точно есть с r180 и до финальной так и осталось. script_game_object_script.cpp: находим: value("on_before_save", int(GameObject::ePostSave)), и заменяем на: value("on_before_save", int(GameObject::eBeforeSave)), Изменено 16 Февраля 2017 пользователем Kondr48 1 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 17 Февраля 2017 (изменено) Исправление сохранения клиентских объектов. Скрытый текст Автор: Shoker Более подробно о ошибке Из за этой опечатки в оригинале есть баг, что при сохранении игры в серверный объект попадают данные клиентского объекта, устаревшие на несколько апдейтов - именно в момент сохранения синхронизации не происходит. (Например если убрать в оружии сохранение патронов у клиентского объекта в CWeaponMagazined::save, оставив сохранение патронов только у серверного объекта - то если выстрелить и сразу сохраниться -> при загрузке число патронов будет прежним) Кто хочет понять природу бага - надо начинать смотреть с функции void CALifeStorageManager::prepare_objects_for_save() в alife_storage_manager.cpp. Она вызывается во время сохранения и в свою очередь вызывает CLevel::ClientSend(), однако там из за опечатки происходит проверка на пропускную способность сети net_HasBandwith() (для МП), которая в 99% возвращает false. Баг не то, чтобы критичный но всё же серьёзный. Исправление: В файле xr_3da\xrGame\Level_network.cpp в начале функции void CLevel::ClientSend() Перед проверкой GameID() == GAME_SINGLE нужно поставить отрицание: if (GameID() != GAME_SINGLE && OnClient()) Фикс вертикальной синхронизации (r2) Скрытый текст Автор: SkyLoader Файл: xr_3da\HW.cpp Найти DevPP.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; и P.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; Затем просто заменить D3DPRESENT_INTERVAL_IMMEDIATE на selectPresentInterval() Добавление изменения переносимого веса в свойства артефактов. Скрытый текст Artifact.h: после: float m_fBleedingRestoreSpeed; добавить: float m_additional_weight; Artifact.cpp: После: SetSlot (ARTEFACT_SLOT); добавить: m_additional_weight = 0.0f; После: m_fBleedingRestoreSpeed = pSettings->r_float(section,"bleeding_restore_speed"); добавить: m_additional_weight = pSettings->r_float(section,"additional_inventory_weight"); ActorCondition.cpp: после: CCustomOutfit* outfit = m_object->GetOutfit(); if(outfit) max_w += outfit->m_additional_weight; добавить: for(TIItemContainer::const_iterator it = object().inventory().m_belt.begin(); object().inventory().m_belt.end() != it; ++it) { CArtefact* cast_artefact = smart_cast<CArtefact*>(*it); if(cast_artefact) max_w += cast_artefact->m_additional_weight; } Чтобы добавляемый вес отображался в описании артефакта. На самом деле это стоит поверять: сработает ли именно так, потому что у себя я просто много уже в этом окошке переписывал Скрытый текст ui_af_params.h: после: _item_bleeding_restore_speed, добавить: _item_additional_inventory_weight, находим: LPCSTR af_item_sect_names[] = { после: "bleeding_restore_speed", добавим: "additional_inventory_weight", находим: LPCSTR af_item_param_names[] = { после: "ui_inv_bleeding", добавим: "ui_inv_additional_inventory_weight", после: LPCSTR _sn = "%"; if(i==_item_radiation_restore_speed || i==_item_power_restore_speed) { _val /= 100.0f; _sn = ""; } добавим: else if (i==_item_additional_inventory_weight) { _val /= 100.0f; _sn = *CStringTable().translate("st_kg"); } Изменено 22 Июня 2017 пользователем Kondr48 1 2 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 28 Февраля 2017 (изменено) А есть ли возможность вывести в xml файл, а из него тогда редактировать загрузочный экран? Это не так просто как кажется, xml он в xr_game, а загрузочный экран в экзешнике, а экзешник он перед xr_game собирается, т. е там надо будет как-то переносить в xr_game это, что скорее всего будет не так просто, как я думаю. Изменено 28 Февраля 2017 пользователем Kondr48 1 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 5 Марта 2017 (изменено) Остается только продумать, как организовать указание движку, откуда иконки читать... Конкретно текстура иконок в нескольких местах читается, там надо просто найти место, где задается иконка и там уже задавать путь к текстуре. А везде где дергается текстура просто функцией вроде get_enqurimet_texture() читать. Сложностей возникнуть не должно, надо просто размотать всю эту цепочку и вникнуть где что и откуда читается и куда присваивается, чтобы свое добавить. Потестил, вроде все как надо. Хотя может можно и по другому ? Да, в принципе, как и надо, я правда не вникал, во всех ли названный случаях вызвается drop, но если все работает, то это хорошо. Изменено 5 Марта 2017 пользователем Kondr48 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 15 Марта 2017 Прошу кого-нибудь на чистой ревизии попробовать по тутору добавить артефакту переносимый вес и отписаться все ли корректно-таки работает. Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 1 Апреля 2017 В файле xray\xr_3da\xrRender\r__screenshot.cpp находим: case IRender_interface::SM_NORMAL: Заменяем: CHK_DX (D3DXSaveSurfaceToFileInMemory (&saved,D3DXIFF_JPG,pFB,0,0)); на: CHK_DX (D3DXSaveSurfaceToFileInMemory (&saved,D3DXIFF_PNG,pFB,0,0)); Теперь расширение, чуть выше меняем: ss_%s_%s_(%s).jpg на, соответственно: ss_%s_%s_(%s).png P. S. на самом деле, кроме формата png есть и другие варианты) typedef enum _D3DXIMAGE_FILEFORMAT { D3DXIFF_BMP = 0, D3DXIFF_JPG = 1, D3DXIFF_TGA = 2, D3DXIFF_PNG = 3, D3DXIFF_DDS = 4, D3DXIFF_PPM = 5, D3DXIFF_DIB = 6, D3DXIFF_HDR = 7, //high dynamic range formats D3DXIFF_PFM = 8, // D3DXIFF_FORCE_DWORD = 0x7fffffff } Т. е, доступны такие расширения, как: bmp, jpg, tga, png, dds, ppm, dib, hdr, pfm. 2 2 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 2 Апреля 2017 Исправление вылета по отсутствию звука. Путь до звука выводится в лог (как с текстурами в ЗП). Автор: Zagolski 1 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 26 Июня 2017 (изменено) Исправление пулестойкости костюмов. Скрытый текст Кто-то слышал, кто-то нет, как разработчики некоторых глобальных модов упоминали (ну по крайней мере я что-то подобное помню), что в движок закралась какая-то чудовищная ошибка и на самом деле бронежилеты от пуль не защищают и чуть ли не чем больше показатель, тем хуже. Однако, в принципе, не так все страшно. На самом деле в этом плане никакой ошибки нет. Просто в Сталкере (речь идет о ТЧ, насчет других частей серии - не уверен, хотя суть та же скорее всего) расчет хита от пули происходит иначе, чем от любого другого воздействия. А конкретно, берутся настройки костей, что позволяет более гибко эту самую пулестойкость настроить. Подробнее можно почитать здесь. Пулестойкость ГГ рассчитывается также, как у НПС. На самом деле, это легко исправить. Возможно ,кому-то не понравится такой способ, ибо если выставить у костюма 100% защиты, то новый костюм будет полностью защищать от пуль (ну пока не износится само собой) не учитывая, что какие-то участки защищены сильнее/слабее. Как по мне, защита по костям, конечно, реалистичнее, но вариант просто в процентах - более играбельный, ведь игрок видит в инвентаре насколько его защищает данный костюм и эта цифра соответствует действительности. Итак. Как сделать работу пулестойкости такой же, как остальные воздействия? Для начала стоит вынести это дело под дефайн, дабы иметь возможность при необходимости включить/отключить данную правку. Для этого, в файл build_config_defines.h нужно добавить: #define FIRE_WOUND_HIT_FIXED // Kondr48: "Фикс" пулестойкости для шлемов и брони. По факту просто выключен механизм расчета хита по костям. Считается также, как остальные хиты. Затем, нам нужен файл EntityCondition.cpp Находим: float CEntityCondition::HitOutfitEffect(float hit_power, ALife::EHitType hit_type, s16 element, float AP) { CInventoryOwner* pInvOwner = smart_cast<CInventoryOwner*>(m_object); if(!pInvOwner) return hit_power; CCustomOutfit* pOutfit = (CCustomOutfit*)pInvOwner->inventory().m_slots[OUTFIT_SLOT].m_pIItem; if(!pOutfit) return hit_power; float new_hit_power = hit_power; if (hit_type == ALife::eHitTypeFireWound) new_hit_power = pOutfit->HitThruArmour(hit_power, element, AP); else new_hit_power *= pOutfit->GetHitTypeProtection(hit_type,element); //увеличить изношенность костюма pOutfit->Hit (hit_power, hit_type); return new_hit_power; } и переписываем вот так: float CEntityCondition::HitOutfitEffect(float hit_power, ALife::EHitType hit_type, s16 element, float AP) { CInventoryOwner* pInvOwner = smart_cast<CInventoryOwner*>(m_object); if(!pInvOwner) return hit_power; CCustomOutfit* pOutfit = (CCustomOutfit*)pInvOwner->inventory().m_slots[OUTFIT_SLOT].m_pIItem; if(!pOutfit) return hit_power; float new_hit_power = hit_power; #ifndef FIRE_WOUND_HIT_FIXED if (hit_type == ALife::eHitTypeFireWound) new_hit_power = pOutfit->HitThruArmour(hit_power, element, AP); else #endif new_hit_power *= pOutfit->GetHitTypeProtection(hit_type,element); //увеличить изношенность костюма pOutfit->Hit (hit_power, hit_type); return new_hit_power; } В принципе, на этом можно было бы и закончить, однако можно под дефайн вынести и неиспользуемые функции в CustomOutfit. Это будет более правильно, потому что исключит неиспользуемые участки кода. Итак, дальше я буду приводить только участки под дефайном. Соответственно, Вам нужно будет найти эту часть в файле и сделать также. 1. CustomOutfit.h Добавим инклуд вначале: #include "..\..\..\build_config_defines.h" #ifndef FIRE_WOUND_HIT_FIXED struct SBoneProtections; #endif #ifndef FIRE_WOUND_HIT_FIXED float HitThruArmour (float hit_power, s16 element, float AP); #endif #ifndef FIRE_WOUND_HIT_FIXED SBoneProtections* m_boneProtection; #endif #ifndef FIRE_WOUND_HIT_FIXED virtual BOOL BonePassBullet (int boneID); #endif 2. CustomOutfit.cpp #ifndef FIRE_WOUND_HIT_FIXED #include "BoneProtections.h" #endif #ifndef FIRE_WOUND_HIT_FIXED m_boneProtection = xr_new<SBoneProtections>(); #endif #ifndef FIRE_WOUND_HIT_FIXED xr_delete(m_boneProtection); #endif Функцию CCustomOutfit::GetHitTypeProtection приводим к такому виду: float CCustomOutfit::GetHitTypeProtection(ALife::EHitType hit_type, s16 element) { float fBase = m_HitTypeProtection[hit_type]*GetCondition(); #ifndef FIRE_WOUND_HIT_FIXED float bone = m_boneProtection->getBoneProtection(element); return 1.0f - fBase*bone; #else return 1.0f - fBase; #endif } #ifndef FIRE_WOUND_HIT_FIXED float CCustomOutfit::HitThruArmour(float hit_power, s16 element, float AP) { float BoneArmour = m_boneProtection->getBoneArmour(element)*GetCondition()*(1-AP); float NewHitPower = hit_power - BoneArmour; if (NewHitPower < hit_power*m_boneProtection->m_fHitFrac) return hit_power*m_boneProtection->m_fHitFrac; return NewHitPower; }; BOOL CCustomOutfit::BonePassBullet (int boneID) { return m_boneProtection->getBonePassBullet(s16(boneID)); }; #endif #ifndef FIRE_WOUND_HIT_FIXED if(pSettings->line_exist(cNameSect(),"bones_koeff_protection")) m_boneProtection->reload( pSettings->r_string(cNameSect(),"bones_koeff_protection"), smart_cast<CKinematics*>(pActor->Visual()) ); #endif Вот и всё, если есть какие-то вопросы - пишите, возможно, я забыл что-то указать. Изменено 26 Июня 2017 пользователем Kondr48 1 2 4 Поделиться этим сообщением Ссылка на сообщение
Kondr48 314 Опубликовано 24 Октября 2017 (изменено) В дополнение к посту @Shkiper2012 Скрытый текст Эту же фичу можно сделать опциональной. case kWPN_ZOOM: Скрытый текст case kWPN_ZOOM: if (IsZoomEnabled()) { if (psActorFlags.test(AF_ZOOM_CLICK_ENABLE)) { if (flags&CMD_START && !IsPending()) OnZoomIn(); else if (IsZoomed()) OnZoomOut(); } else { if( flags&CMD_START && !IsPending() && !IsZoomed() ) OnZoomIn(); else if( flags&CMD_START && IsZoomed() ) OnZoomOut(); } return true; } else return false; Затем, добавляем новый флаг в Actor_Flags.h AF_ZOOM_CLICK_ENABLE = (1<<11), И регистрируем консольную команду в файле console_commands.cpp void CCC_RegisterCommands() { ... CMD1(CCC_GameDifficulty, "g_game_difficulty" ); CMD3(CCC_Mask, "zoom_click_enable", &psActorFlags, AF_ZOOM_CLICK_ENABLE); ... } Осталось лишь зарегистрировать новый пункт меню в файлах игры. 1. ui_mm_opt_gameplay.script : _st = xml:InitStatic("tab_gameplay:templ_item", self.scroll_v) xml:InitCheck("tab_gameplay:check_zoom_click_enable", _st) 2. ui_mm_opt(_16).xml: <check_zoom_click_enable x="10" y="0" width="243" height="21"> <options_item entry="zoom_click_enable" group="mm_opt_gameplay"/> <text font="letterica18" r="215" g="195" b="170">ui_mm_zoom_click_enable</text> </check_zoom_click_enable> 3. Любой текстовый файл: <string id="ui_mm_zoom_click_enable"> <text>Удерживать ($$ACTION_WPN_ZOOM$$) для прицеливания</text> </string> Изменено 24 Октября 2017 пользователем Kondr48 2 3 2 Поделиться этим сообщением Ссылка на сообщение