Gość Diegtiariow Opublikowano 3 Marca 2023 Zgłoś Udostępnij Opublikowano 3 Marca 2023 Na chwilę obecną temat jest skierowany do nowicjuszy. Tutaj będą zebrane gotowe rozwiązania dot. grzebania w kodach źródłowych silnika SoC. Właściwie zapraszam wszystkich do przyłączenia się. TWORZENIE CALLBACK'ÓW DLA AKTORA NA PRZYKŁADZIE CALLBACK'A DŹGNIĘCIA NOŻEM: Spoiler Callback - to funkcja, która jest wywoływana przy określonej akcji, np. naciśnięcie klawisza, wystrzał z broni, śmierć aktora, itd. Tworzenie takiego callback'a nie jest trudne. Rozważmy przykład tworzenia callback'ów na ciosy nożem na PPM i LPM. Żadne argumenty nie zostaną tu przekazane, tylko wywołanie funkcji przy ciosie. Więc. 1. W pliku WeaponKnife.cpp dodajemy na początek pliku po wszystkich include'ach jeszcze 4 include'y potrzebne dla callback'ów aktora: #include "pch_script.h" #include "script_callback_ex.h" #include "script_game_object.h" #include "alife_object_registry.h" 2. Dalej szukamy: void CWeaponKnife::switch2_Attacking (u32 state) i po: m_pHUD->animPlay(random_anim(mhud_attack), FALSE, this, state); piszemy: g_actor->callback(GameObject::eKnifeAttackOne)(); Analogicznie po: m_pHUD->animPlay(random_anim(mhud_attack2), FALSE, this, state); piszemy: g_actor->callback(GameObject::eKnifeAttackTwo)(); 3. Nasze callback'i trzeba zarejestrować: - w pliku: game_object_space.h dodajemy do listy callback'ów analogicznie na koniec: eKnifeAttackOne, eKnifeAttackTwo, w script_game_object_script.cpp analogicznie jak u innych callback'ów dodajemy na koniec: value("knife_attack_one", int(GameObject::eKnifeAttackOne) ), value("knife_attack_two", int(GameObject::eKnifeAttackTwo) ) Warto tutaj trochę wyjaśnić: w cudzysłowie jest ten identyfikator, który rejestrujemy w bind_stalker.script. Rejestracja callback'a w bind_stalker.script. Do funkcji: function actor_binder:net_destroy() dodajemy: function actor_binder:reinit() dodajemy też: 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) I dodajemy funkcje, które będą wywoływane po wykonaniu callback'a: function actor_binder:knife_attack_one() news_manager.send_tip(db.actor, "Cios LPM") --wiadomość dla testu end function actor_binder:knife_attack_two() news_manager.send_tip(db.actor, "Cios PPM") --wiadomość dla testu end TWORZENIE FUNKCJI SORTUJĄCEJ PRZEDMIOTY NA PASIE: Spoiler Wiele ludzi spotkało się z funkcją iterate_inventory, która sortuje cały ekwipunek i dla każdego wywołuje funkcję. Jeśli potrzebujemy posortować wszystkie przedmioty na pasie, aby sprawdzić, czy jest tam jakiś przedmiot lub nie, można oczywiście, posortować cały ekwipunek i sprawdzać dla każdego przedmiotu na pasie czy on jest czy go nie ma. Ale można też stworzyć oddzielną funkcję, która będzie sortować tylko przedmioty na pasie. 1. W pliku: script_game_object_script3.cpp szukamy iterate_inventory i analogicznie robimy wariant dla pasa: .def("iterate_inventory", &CScriptGameObject::IterateInventory) .def("iterate_belt", &CScriptGameObject::IterateBelt) Teraz w script_game_object.h rejestrujemy naszą funkcję: void IterateInventory (luabind::functor<void> functor, luabind::object object); void IterateBelt (luabind::functor<void> functor, luabind::object object); I na koniec, w pliku script_game_object_inventory_owner.cpp dodajemy samą funkcję. Znajdziecie funkcję dla inwentarza, zróbcie również. 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()); } Jak widzicie, różnica tutaj jest tylko w tym, że przedmioty są sortowane nie wszystkie, a tylko te, które są na pasie: tak jak zamiast m_all, używamy m_belt. Jak wykorzystać w skryptach: Używa się tak samo, jak funkcji iterate_inventory. POPRAWKA NA WYŚWIETLANIE ZIELONYM KOLOREM PRZECZYTANYCH NOTATEK W DZIALE PDA "DZIENNIK" BY LVG_BREST: Spoiler 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; } } } } } } Pod koniec UIDiaryWnd2.cpp dopisać: // Poprawka na wyświetlanie zielonym kolorem przeczytanych notatek w dzienniku PDA 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); // Poprawka na wyświetlanie zielonym kolorem przeczytanych notatek w dzienniku PDA void AddDiaryArticle (shared_str, bool bReaded); TWORZENIE NOWEGO SLOTU: Spoiler W tej części użyto rewizji z XP DEV, ponieważ zostały tam przeprowadzone prace nad slotami. Jeśli ktoś korzysta z innej wersji źródeł, mogą być różnice. Tworzymy slot dla drugiego detektora: W UIInventoryWnd.cpp po: 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); dodajemy: 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); Po: m_slots_array[SLOT_QUICK_ACCESS_3] = m_pUISlotQuickAccessList_3; dodajemy: m_slots_array[DET_ADV_SLOT] = m_pUIDetAdvList; W UIInventoryWnd.h po: CUIDragDropListEx* m_pUISlotQuickAccessList_3; dodajemy: CUIDragDropListEx* m_pUIDetAdvList; W pliku uiinventorywnd2.cpp od razu po: _itm = m_pInv->m_slots[SLOT_QUICK_ACCESS_3].m_pIItem; if(_itm) { CUICellItem* itm = create_cell_item(_itm); m_pUISlotQuickAccessList_3->SetItem (itm); } dodajemy: _itm = m_pInv->m_slots[DET_ADV_SLOT].m_pIItem; if(_itm) { CUICellItem* itm = create_cell_item(_itm); m_pUIDetAdvList->SetItem (itm); } Po: m_pUISlotQuickAccessList_3->ClearAll (true); dodajemy: m_pUIDetAdvList->ClearAll (true); W pliku UIInventoryWnd3.cpp po: case INVENTORY_TO_SLOT15_ACTION: CurrentIItem()->SetSlot(SLOT_QUICK_ACCESS_3); break; dodajemy: case INVENTORY_TO_SLOT16_ACTION: CurrentIItem()->SetSlot(DET_ADV_SLOT); break; W pliku inventory_space.h po: #define SLOT_QUICK_ACCESS_3 15 dodajemy: #define DET_ADV_SLOT 16 Również wnosimy zmiany w slots_total. Numer musi być o jeden większy niż numer ostatniego slotu. W naszym przypadku będzie to wyglądało tak: #define SLOTS_TOTAL 17 W pliku UIMessages.h po: INVENTORY_TO_SLOT15_ACTION, piszemy: INVENTORY_TO_SLOT16_ACTION, Teraz pozostało tylko zarejestrować nam, nowy slot w plikach konfiguracyjnych gry, analogicznie do pozostałych. W inventory_new.xml i w 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"/> I w 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 NOWE PARAMETRY DLA PANCERZY: Spoiler W CoP niektóre modyfikacje pancerzy zwiększają szybkość regeneracji zdrowia, energii, itd. W oryginalnym SoC jest parę unikalnych pancerzy, w których jest parametr health_restore_speed, ale oczywiście on nie działa. Dodać te parametry można bardzo łatwo: W CustomOutfit.cpp po: m_HitTypeProtection[ALife::eHitTypePhysicStrike]= READ_IF_EXISTS(pSettings, r_float, section, "physic_strike_protection", 0.0f); dodajemy: 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 ); Od razu warto zwrócić uwagę, że jeżeli w pliku konfiguracyjnym będzie brakować jednego z nowych parametrów, to jego wartość będzie równa 0.0. Idziemy dalej. W pliku CustomOutfit.h po: public: float m_additional_weight; float m_additional_weight2; dodajemy: float m_fHealthRestoreSpeed; float m_fRadiationRestoreSpeed; float m_fSatietyRestoreSpeed; float m_fPowerRestoreSpeed; float m_fBleedingRestoreSpeed; Teraz te parametry są czytane, ale na razie nie są wykorzystywane. Dla ich użycia w Actor.cpp po, np.: 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; } dodajemy: #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 } } Teraz po: //dla właściwości artefaktów, znajdujących się na pasie UpdateArtefactsOnBelt (); dodajemy: UpdtateOutfitInSlot (); Teraz w Actor.h po: //właściwości artefaktów 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;} dodajemy: //parametry pancerza virtual void UpdtateOutfitInSlot (); PRZYPISYWANIE SKRÓTÓW KLAWISZOWYCH DO SKRYPTÓW, KTÓRE MOŻNA ZMIENIAĆ W MENU GŁÓWNYM: Spoiler Stworzymy przycisk, który będzie można z łatwością zmieniać w opcjach, a po naciśnięciu będzie wywoływana funkcja ze skryptu. W pliku: ActorInput.cpp po: #include "../../build_config_defines.h" dodajemy: #include "pch_script.h" #include "InventoryOwner.h" #include "script_game_object.h" #include "script_game_object_impl.h" Nie wiadomo czy są potrzebne wszystkie 4 include'y, ale na wszelki wypadek lepiej napisać wszystkie 4. Również np. po: case kCROUCH_TOGGLE: { g_bAutoClearCrouch = !g_bAutoClearCrouch; if (!g_bAutoClearCrouch) mstate_wishful |= mcCrouch; }break; dodajemy: case kCLOCK: { luabind::functor<void> clock_key; if (ai().script_engine().functor("gz_items_hud.clock_key",clock_key)) clock_key(); }break; Jak widzicie, jest tutaj przycisk wyświetlenia godziny. "gz_items_hud.clock_key" to skrypt i funkcja, które zostały wywołane po naciśnięciu przycisku. Teraz w pliku key_binding_registrator_script.cpp w: class_<enum_exporter<EGameActions> >("key_bindings") .enum_("commands") [ analogicznie do pozostałych dodajemy: value("kCLOCK", int(kCLOCK)), Jeśli będziecie dodawać na koniec, to nie zapomnijcie usunąć przecinka na końcu linijki. Potem w pliku: xr_level_controller.cpp dodajemy analogicznie do pozostałych: { "clock", kCLOCK ,_sp}, Prawdopodobnie _sp odnosi się do trybu jednego gracza, _both do obydwu, a _mp do wieloosobowego. Nie wiadomo, trzeba to sprawdzić. Teraz w xr_level_controller.h dodajemy: kCLOCK, do innych przycisków. Nawiasem mówiąc takim sposobem, jeśli jest to potrzebne, można zamienić jakąś silnikową akcję na skryptową. Po prostu szukamy case kPOTRZEBNY_PRZYCISK: i zmieniamy "potrzebny" na swój. I to wszystko, przycisk w silniku jest. Przechodzimy do konfiguracji. Plik u\ui_keybinding.xml dodajemy do żądanej grupy przycisków: <command id="kb_clock" exe="clock"/> Teraz w test\rus\ui_st_keybinding.xml: <string id="kb_clock"> <text>Zegar</text> </string> Jeśli chcecie, aby domyślnie przycisk był już ustawiony, szukamy pliku default_controls.ltx w folderze config, skąd są brane dane o tym, jaki przycisk na jakie działanie ustawić przy tworzeniu przez grę pliku user.ltx. Po prostu piszemy: bind clock kC Funkcja w skrypcie jest najzwyklejsza: --//*** Zegar na ręce function clock_key() -- wywołuje się z silnika po naciśnięciu przycisku zegar --działanie po naciśnięciu przycisku end I to wszystko. Nie sprawdzano, co będzie jeśli skrypta lub funkcji nie ma. PRZYWRÓCENIE FUNKCJI ODRADZANIA ARTEFAKTÓW PO ZADZIAŁANIU ANOMALII (AUTOR: BAK): Spoiler Źródło: https://disk.yandex.ru/d/LYCNOX-buFd7a Dodano parametry dla anomalii: ; ustawienia online spawnu spawn_blowout_artefacts = on ; główny przełącznik możliwości respawnu artefact_spawn_probability = 0.05 ;prawdopodobieństwo, że podczas działania anomalii pojawi się artefakt ; dodane przez ? birth_on_death_probability = 0.2 ; prawdopodobieństwo pojawienia się artefaktu po śmierci mutanta/NPC w anomalii birth_on_torn_probability = 0.5 ; prawdopodobieństwo pojawienia się artefaktu po rozerwaniu ciała birth_on_nonalive = true ; możliwość pojawienia się po działaniu na przedmiot birth_on_alive = true ; możliwość pojawienia się po działaniu na żywą istotę birth_on_dead = true ; możliwość pojawienia się po działaniu na trupa ŹRÓDŁO: https://ap-pro.ru/forums/topic/1572-soc-melkie-pravki-dvizhka/ Cytuj Odnośnik do komentarza Udostępnij na innych stronach Więcej opcji udostępniania...
Rekomendowane odpowiedzi
Dołącz do dyskusji
Możesz dodać zawartość już teraz a zarejestrować się później. Jeśli posiadasz już konto, zaloguj się aby dodać zawartość za jego pomocą.
Uwaga: Twój wpis zanim będzie widoczny, będzie wymagał zatwierdzenia moderatora.