Ковыряясь на форумах находил много интересных постов, касательно готовых исходников правок. Сейчас появилось свободное время, в связи с чем решил собрать их в этой теме. Конечно же, тут собрано не всё, в ближайшее время(возможно) будет дополнено. Большая часть правок портируется под другие версии, так что, если я не написал вариант для используемой вами платформы, вы можете попробовать сделать порт сами. Особенно это относится к 1.5.10, т.к. копи-пастить куски кода я не счёл нужным.
1.0007(rc1) от Alpet & KD
Vampir35:
Ошибка в before save
Ошибка-опечатка в оригинальном репозитории xp-dev в каллбеке на before save (вызывается перед созданием сейва, насколько я понял), точно есть с r180 и до финальной так и осталось.
script_game_object_script.cpp:
находим:
Код
value("on_before_save", int(GameObject::ePostSave)),
и заменяем на:
Код
value("on_before_save", int(GameObject::eBeforeSave)),
Создание нового слота
Я использую ревизию с 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);
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);
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);
}
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);
}
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;
CurrentIItem()->SetSlot(SLOT_QUICK_ACCESS_3);
break;
добавим:
Код
case INVENTORY_TO_SLOT16_ACTION:
CurrentIItem()->SetSlot(DET_ADV_SLOT);
break;
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
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
1.0007(rc1)
Zagolski:
Фикс вылета звука на ТЧ
Код
void CSoundRender_Source::load(LPCSTR name)
{
string_path fn,N;
strcpy (N,name);
strlwr (N);
if(strext(N)) *strext(N) = 0;
fname = N;
strconcat (sizeof(fn),fn,N,".ogg");
if(!FS.exist("$level$",fn)) FS.update_path (fn,"$game_sounds$",fn);
if (!FS.exist(fn))
{
Msg("! Can't find sound '%s.ogg'",N);
FS.update_path (fn,"$game_sounds$","$no_sound.ogg");
}
LoadWave (fn);
SoundRender->cache.cat_create (CAT, dwBytesTotal);
}[code][/hide]
[i]Shoker[/i]:
[hide=Сохранение клиентских объектов]
В файле xr_3da\xrGame\Level_network.cpp в начале функции
void CLevel::ClientSend()
Перед проверкой [b]GameID() == GAME_SINGLE[/b] нужно поставить отрицание:
[code]if (GameID() != GAME_SINGLE && OnClient())
{
string_path fn,N;
strcpy (N,name);
strlwr (N);
if(strext(N)) *strext(N) = 0;
fname = N;
strconcat (sizeof(fn),fn,N,".ogg");
if(!FS.exist("$level$",fn)) FS.update_path (fn,"$game_sounds$",fn);
if (!FS.exist(fn))
{
Msg("! Can't find sound '%s.ogg'",N);
FS.update_path (fn,"$game_sounds$","$no_sound.ogg");
}
LoadWave (fn);
SoundRender->cache.cat_create (CAT, dwBytesTotal);
}[code][/hide]
[i]Shoker[/i]:
[hide=Сохранение клиентских объектов]
В файле xr_3da\xrGame\Level_network.cpp в начале функции
void CLevel::ClientSend()
Перед проверкой [b]GameID() == GAME_SINGLE[/b] нужно поставить отрицание:
[code]if (GameID() != GAME_SINGLE && OnClient())
Более подробно о ошибке
Из за этой опечатки в оригинале есть баг, что при сохранении игры в серверный объект попадают данные клиентского объекта, устаревшие на несколько апдейтов - именно в момент сохранения синхронизации не происходит. (Например если убрать в оружии сохранение патронов у клиентского объекта в CWeaponMagazined::save, оставив сохранение патронов только у серверного объекта - то если выстрелить и сразу сохраниться -> при загрузке число патронов будет прежним)
Кто хочет понять природу бага - надо начинать смотреть с функции void CALifeStorageManager::prepare_objects_for_save() в alife_storage_manager.cpp. Она вызывается во время сохранения и в свою очередь вызывает CLevel::ClientSend(), однако там из за опечатки происходит проверка на пропускную способность сети net_HasBandwith() (для МП), которая в 99% возвращает false.
Баг не то, чтобы критичный но всё же серьёзный.
lvg_brest:
Исправление не отключения света после выключения аномалии
CustomZone.cpp
Код
//загрузить параметры световой вспышки от взрыва
m_zone_flags.set(eBlowoutLight, pSettings->r_bool (section, "blowout_light"));
if(m_zone_flags.test(eBlowoutLight) ){
sscanf(pSettings->r_string(section,"light_color"), "%f,%f,%f", &m_LightColor.r, &m_LightColor.g, &m_LightColor.b);
m_fLightRange = pSettings->r_float(section,"light_range");
m_fLightTime = pSettings->r_float(section,"light_time");
m_fLightTimeLeft = 0.f; // Правильно задать тип переменной
m_fLightHeight = pSettings->r_float(section,"light_height");
}
void CCustomZone::UpdateBlowoutLight ()
{
if(m_fLightTimeLeft>0.f)
{
m_fLightTimeLeft -= Device.fTimeDelta;
// Исправление не отключения света после выключения аномалии
if (m_fDistanceToCurEntity>29.f)
if (m_fLightTime<=1.f)
m_fLightTimeLeft = m_fLightTimeLeft/1.45f;
else
m_fLightTimeLeft = m_fLightTimeLeft/1.15f;
clamp(m_fLightTimeLeft,0.0f,m_fLightTime);
....
m_zone_flags.set(eBlowoutLight, pSettings->r_bool (section, "blowout_light"));
if(m_zone_flags.test(eBlowoutLight) ){
sscanf(pSettings->r_string(section,"light_color"), "%f,%f,%f", &m_LightColor.r, &m_LightColor.g, &m_LightColor.b);
m_fLightRange = pSettings->r_float(section,"light_range");
m_fLightTime = pSettings->r_float(section,"light_time");
m_fLightTimeLeft = 0.f; // Правильно задать тип переменной
m_fLightHeight = pSettings->r_float(section,"light_height");
}
void CCustomZone::UpdateBlowoutLight ()
{
if(m_fLightTimeLeft>0.f)
{
m_fLightTimeLeft -= Device.fTimeDelta;
// Исправление не отключения света после выключения аномалии
if (m_fDistanceToCurEntity>29.f)
if (m_fLightTime<=1.f)
m_fLightTimeLeft = m_fLightTimeLeft/1.45f;
else
m_fLightTimeLeft = m_fLightTimeLeft/1.15f;
clamp(m_fLightTimeLeft,0.0f,m_fLightTime);
....
Исправление отображения параметра "отношение" при обыске тел монстров. Исправление заливки иконки красным цветом при обыске тел монстров и людей
1) UICarBodyWnd.cpp
Код
void CUICarBodyWnd::InitCarBody(CInventoryOwner* pOur, CInventoryOwner* pOthers)
{
m_pOurObject = pOur;
m_pOthersObject = pOthers;
m_pInventoryBox = NULL;
u16 our_id = smart_cast<CGameObject*>(m_pOurObject)->ID();
u16 other_id = smart_cast<CGameObject*>(m_pOthersObject)->ID();
m_pUICharacterInfoLeft->InitCharacter (our_id);
m_pUIOthersIcon->Show (true);
// Исправление отображения параметра "отношение" при обыске тел монстров
m_pUICharacterInfoRight->ClearFlags ();
CBaseMonster *monster = NULL;
if(m_pOthersObject) {
monster = smart_cast<CBaseMonster *>(m_pOthersObject);
if (monster || m_pOthersObject->use_simplified_visual() )
{
m_pUICharacterInfoRight->ClearInfo ();
if(monster)
{
// Исправление отображения параметра "отношение" при обыске тел монстров
m_pUICharacterInfoRight->SetMonsterFlag ();
shared_str monster_tex_name = pSettings->r_string(monster->cNameSect(),"icon");
m_pUICharacterInfoRight->UIIcon().InitTexture(monster_tex_name.c_str());
m_pUICharacterInfoRight->UIIcon().SetStretchTexture(true);
}
}else
{
// Исправление заливки иконки красным цветом при обыске тел монстров и людей
m_pUICharacterInfoRight->SetHumanFlag ();
m_pUICharacterInfoRight->InitCharacter (other_id);
}
}
m_pUIPropertiesBox->Hide ();
EnableAll ();
UpdateLists ();
....
{
m_pOurObject = pOur;
m_pOthersObject = pOthers;
m_pInventoryBox = NULL;
u16 our_id = smart_cast<CGameObject*>(m_pOurObject)->ID();
u16 other_id = smart_cast<CGameObject*>(m_pOthersObject)->ID();
m_pUICharacterInfoLeft->InitCharacter (our_id);
m_pUIOthersIcon->Show (true);
// Исправление отображения параметра "отношение" при обыске тел монстров
m_pUICharacterInfoRight->ClearFlags ();
CBaseMonster *monster = NULL;
if(m_pOthersObject) {
monster = smart_cast<CBaseMonster *>(m_pOthersObject);
if (monster || m_pOthersObject->use_simplified_visual() )
{
m_pUICharacterInfoRight->ClearInfo ();
if(monster)
{
// Исправление отображения параметра "отношение" при обыске тел монстров
m_pUICharacterInfoRight->SetMonsterFlag ();
shared_str monster_tex_name = pSettings->r_string(monster->cNameSect(),"icon");
m_pUICharacterInfoRight->UIIcon().InitTexture(monster_tex_name.c_str());
m_pUICharacterInfoRight->UIIcon().SetStretchTexture(true);
}
}else
{
// Исправление заливки иконки красным цветом при обыске тел монстров и людей
m_pUICharacterInfoRight->SetHumanFlag ();
m_pUICharacterInfoRight->InitCharacter (other_id);
}
}
m_pUIPropertiesBox->Hide ();
EnableAll ();
UpdateLists ();
....
void CUICarBodyWnd::Hide()
{
InventoryUtilities::SendInfoToActor ("ui_car_body_hide");
m_pUIOurBagList->ClearAll (true);
m_pUIOthersBagList->ClearAll (true);
// Исправление отображения параметра "отношение" при обыске тел монстров
m_pUICharacterInfoRight->ClearFlags ();
inherited::Hide ();
if(m_pInventoryBox)
m_pInventoryBox->m_in_use = false;
}
2) UICharacterInfo.cpp
Код
// Исправление отображения параметра "отношение" при обыске тел монстров
bool MonsterFlag = false;
bool HumanFlag = false;
void CUICharacterInfo::Update()
{
inherited::Update();
if(hasOwner() && (m_bForceUpdate||(Device.dwFrame%100==0)) ){
m_bForceUpdate = false;
CSE_ALifeTraderAbstract* T = ch_info_get_from_id (m_ownerID);
if (NULL==T){
m_ownerID = u16(-1);
return;
}else
UpdateRelation();
if(m_icons[eUIIcon]){
CSE_ALifeCreatureAbstract* pCreature = smart_cast<CSE_ALifeCreatureAbstract*>(T);
// Исправление отображения параметра "отношение" при обыске тел монстров
if((pCreature && !pCreature->g_Alive() && MonsterFlag) || (Actor()->ID()==m_ownerID || !hasOwner()))
{
if(m_icons[eUIRelationCaption]) m_icons[eUIRelationCaption]->Show (false);
if(m_icons[eUIRelation]) m_icons[eUIRelation]->Show (false);
}
// Исправление заливки иконки красным цветом при обыске тел монстров и людей
if(pCreature && !pCreature->g_Alive() && !MonsterFlag && !HumanFlag)
{
m_icons[eUIIcon]->SetColor (color_argb(255,255,160,160));
}
}
}
}
bool MonsterFlag = false;
bool HumanFlag = false;
void CUICharacterInfo::Update()
{
inherited::Update();
if(hasOwner() && (m_bForceUpdate||(Device.dwFrame%100==0)) ){
m_bForceUpdate = false;
CSE_ALifeTraderAbstract* T = ch_info_get_from_id (m_ownerID);
if (NULL==T){
m_ownerID = u16(-1);
return;
}else
UpdateRelation();
if(m_icons[eUIIcon]){
CSE_ALifeCreatureAbstract* pCreature = smart_cast<CSE_ALifeCreatureAbstract*>(T);
// Исправление отображения параметра "отношение" при обыске тел монстров
if((pCreature && !pCreature->g_Alive() && MonsterFlag) || (Actor()->ID()==m_ownerID || !hasOwner()))
{
if(m_icons[eUIRelationCaption]) m_icons[eUIRelationCaption]->Show (false);
if(m_icons[eUIRelation]) m_icons[eUIRelation]->Show (false);
}
// Исправление заливки иконки красным цветом при обыске тел монстров и людей
if(pCreature && !pCreature->g_Alive() && !MonsterFlag && !HumanFlag)
{
m_icons[eUIIcon]->SetColor (color_argb(255,255,160,160));
}
}
}
}
В конце UICharacterInfo.cpp (после void CUICharacterInfo::ClearInfo()) дописать:
Код
// Исправление отображения параметра "отношение" при обыске тел монстров
void CUICharacterInfo::SetMonsterFlag()
{
MonsterFlag = true;
}
void CUICharacterInfo::SetHumanFlag()
{
HumanFlag = true;
}
void CUICharacterInfo::ClearFlags()
{
MonsterFlag = false;
HumanFlag = false;
}
void CUICharacterInfo::SetMonsterFlag()
{
MonsterFlag = true;
}
void CUICharacterInfo::SetHumanFlag()
{
HumanFlag = true;
}
void CUICharacterInfo::ClearFlags()
{
MonsterFlag = false;
HumanFlag = false;
}
3) UICharacterInfo.h
Код
public:
...
u16 OwnerID () const {return m_ownerID;}
CUIStatic& UIIcon () {VERIFY(m_icons[eUIIcon]);return *m_icons[eUIIcon];}
CUIStatic& UIName() {VERIFY(m_icons[eUIName]);return *m_icons[eUIName];}
const shared_str& IconName () {return m_texture_name;}
// Исправление отображения параметра "отношение" при обыске тел монстров
void SetMonsterFlag ();
void SetHumanFlag ();
void ClearFlags ();
};
...
u16 OwnerID () const {return m_ownerID;}
CUIStatic& UIIcon () {VERIFY(m_icons[eUIIcon]);return *m_icons[eUIIcon];}
CUIStatic& UIName() {VERIFY(m_icons[eUIName]);return *m_icons[eUIName];}
const shared_str& IconName () {return m_texture_name;}
// Исправление отображения параметра "отношение" при обыске тел монстров
void SetMonsterFlag ();
void SetHumanFlag ();
void ClearFlags ();
};
Исправление отображения зеленым цветом прочитанных записей в разделе КПК "Дневник
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() && 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;
}
}
}
{
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() && 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);
}
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);
}
Код
[b]2)[/b] UIDiaryWnd.h
void UnloadNewsTab ();
void LoadNewsTab ();
void Reload (EDiaryFilter new_filter);
// Исправление отображения зеленым цветом прочитанных записей в дневнике КПК
void AddDiaryArticle (shared_str, bool bReaded);
void UnloadNewsTab ();
void LoadNewsTab ();
void Reload (EDiaryFilter new_filter);
// Исправление отображения зеленым цветом прочитанных записей в дневнике КПК
void AddDiaryArticle (shared_str, bool bReaded);
Исправление учета статистики убийства сталкеров и мутантов, выполненных квестов
actor_statistic_mgr.cpp
Код
void CActorStatisticMgr::AddPoints(const shared_str& key, const shared_str& detail_key, s32 cnt, s32 pts)
{
SStatSectionData& sect = GetSection (key);
SStatDetailBData& d = sect.GetData (detail_key);
d.int_count += cnt;
// Исправление учета статистики убийства сталкеров и мутантов, выполненных квестов
d.int_points = pts;
}
{
SStatSectionData& sect = GetSection (key);
SStatDetailBData& d = sect.GetData (detail_key);
d.int_count += cnt;
// Исправление учета статистики убийства сталкеров и мутантов, выполненных квестов
d.int_points = pts;
}
Исправление работы статика биографии в разделе КПК "Ранги
UICharacterInfo.cpp
Код
if (chInfo.Bio().size())
{
CUIStatic* pItem = xr_new<CUIStatic>();
pItem->SetWidth (pUIBio->GetDesiredChildWidth());
// Исправление работы статика биографии
pItem->SetTextComplexMode (true);
pItem->SetText (*(chInfo.Bio()));
pItem->AdjustHeightToText ();
pUIBio->AddWindow (pItem, true);
}
{
CUIStatic* pItem = xr_new<CUIStatic>();
pItem->SetWidth (pUIBio->GetDesiredChildWidth());
// Исправление работы статика биографии
pItem->SetTextComplexMode (true);
pItem->SetText (*(chInfo.Bio()));
pItem->AdjustHeightToText ();
pUIBio->AddWindow (pItem, true);
}
Исправление "залипания" курсора мыши в главном меню после просмотра титров
1) UIGameTutorialSimpleItem.cpp
Код
bool CUISequenceSimpleItem::Stop (bool bForce)
{
if(!m_flags.test(etiCanBeStopped)&&!bForce)
return false;
m_owner->MainWnd()->DetachChild (m_UIWindow);
m_sound.stop ();
if(m_flags.test(etiNeedPauseOn) && !m_flags.test(etiStoredPauseState))
Device.Pause (FALSE, TRUE, FALSE, "simpleitem_stop");
if(m_flags.test(etiNeedPauseOff) && m_flags.test(etiStoredPauseState))
Device.Pause (TRUE, TRUE, FALSE, "simpleitem_stop");
if(m_flags.test(etiNeedPauseSound))
Device.Pause (FALSE, FALSE, TRUE, "simpleitem_stop");
// Исправление "залипания" курсора мыши в главном меню после просмотра титров
if (!g_pGameLevel){
GetUICursor()->SetUICursorPosition (m_desired_cursor_pos);
}
if (g_pGameLevel){
CUIGameSP* ui_game_sp = smart_cast<CUIGameSP*>(HUD().GetUI()->UIGame());
if( ui_game_sp && ui_game_sp->PdaMenu->IsShown() )
HUD().GetUI()->StartStopMenu (ui_game_sp->PdaMenu, true);
}
inherited::Stop ();
return true;
}
{
if(!m_flags.test(etiCanBeStopped)&&!bForce)
return false;
m_owner->MainWnd()->DetachChild (m_UIWindow);
m_sound.stop ();
if(m_flags.test(etiNeedPauseOn) && !m_flags.test(etiStoredPauseState))
Device.Pause (FALSE, TRUE, FALSE, "simpleitem_stop");
if(m_flags.test(etiNeedPauseOff) && m_flags.test(etiStoredPauseState))
Device.Pause (TRUE, TRUE, FALSE, "simpleitem_stop");
if(m_flags.test(etiNeedPauseSound))
Device.Pause (FALSE, FALSE, TRUE, "simpleitem_stop");
// Исправление "залипания" курсора мыши в главном меню после просмотра титров
if (!g_pGameLevel){
GetUICursor()->SetUICursorPosition (m_desired_cursor_pos);
}
if (g_pGameLevel){
CUIGameSP* ui_game_sp = smart_cast<CUIGameSP*>(HUD().GetUI()->UIGame());
if( ui_game_sp && ui_game_sp->PdaMenu->IsShown() )
HUD().GetUI()->StartStopMenu (ui_game_sp->PdaMenu, true);
}
inherited::Stop ();
return true;
}
KD:
Окна редактора
в xrCore::_initialize и _destroy уберите
Код
CoInitializeEx (NULL, COINIT_MULTITHREADED)
Иначе GetOpenFileName/GetSaveFileName повисают на висте и выше.
SkyLoader:
Не работает вертикальная синхронизация (r2)
HW.cpp:
Код
DevPP.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
И
Код
P.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
Меняем D3DPRESENT_INTERVAL_IMMEDIATE на selectPresentInterval()
Очень странный вылет
Данная проблема была в свое в ЛА еще в 2013 году, и я очень долго не мог найти причину вылета, так как вылет каждый раз ссылался на разные некорректные по его мнению объекты. Причем возникал не всегда и можно было его поймать только при выходе из игры. Спустя несколько месяцев чудом проблема была найдена. Оказывается, проблема была в лампочках, которые включались и выключались скриптово. Игра ловила крэш при 255 и более включении и выключении лампочки. Фиксится в HangingLamp.cpp в строках:
Код
}
if(!PPhysicsShell())//if we have physiccs_shell it will call processing deactivate when disable
processing_deactivate ();
if(!PPhysicsShell())//if we have physiccs_shell it will call processing deactivate when disable
processing_deactivate ();
Удаляем if(!PPhysicsShell()), теперь processing_deactivate(); будет срабатывать всегда, как и должен, и мы можем включать и выключать лампочки сколько угодно раз.
Фикс полосы загрузки
x_ray.cpp:
Код
if (g_pGamePersistent->GameType()==1 && strstr(Core.Params,"alife"))
max_load_stage = 17;
else
max_load_stage = 14;
max_load_stage = 17;
else
max_load_stage = 14;
Как можно заметить по названию, max_load_stage, это переменная, которая показывает количество стадий загрузки, т.е. загрузка моделей, шейдеров, звуков, синхронизация объектов и тд. В сингле их целых 17, в мультиплеере 14.
Вот эту переменную потом получает рендер и рисует шкалу загрузки в соответствии с текущей стадией загрузки. Но тут есть ошибка, а именно сравнение strstr(Core.Params,"alife"). Но у нас никогда не происходит такого, так как никто не пишет батник запуска игры с добавлением alife. Именно это и проверяется. Из-за этого для игры переменная max_load_stage всегда равна 14. Поэтому в ЗП полоса загрузки очень быстро достигает конца и долго висит.
Код
if (g_pGamePersistent->GameType()==1 && !xr_strcmp(p.m_alife, "alife"))
max_load_stage = 17;
else
max_load_stage = 14;
max_load_stage = 17;
else
max_load_stage = 14;
Полтергейст:
Vampir35:
Исправление пулестойкости костюмов SHoC
Кто-то слышал, кто-то нет, как разработчики некоторых глобальных модов упоминали (ну по крайней мере я что-то подобное помню), что в движок закралась какая-то чудовищная ошибка и на самом деле бронежилеты от пуль не защищают и чуть ли не чем больше показатель, тем хуже. Однако, в принципе, не так все страшно. На самом деле в этом плане никакой ошибки нет. Просто в Сталкере (речь идет о ТЧ, насчет других частей серии - не уверен, хотя суть та же скорее всего) расчет хита от пули происходит иначе, чем от любого другого воздействия. А конкретно, берутся настройки костей, что позволяет более гибко эту самую пулестойкость настроить. Подробнее можно почитать здесь. Пулестойкость ГГ рассчитывается также, как у НПС.
На самом деле, это легко исправить. Возможно ,кому-то не понравится такой способ, ибо если выставить у костюма 100% защиты, то новый костюм будет полностью защищать от пуль (ну пока не износится само собой) не учитывая, что какие-то участки защищены сильнее/слабее.
Как по мне, защита по костям, конечно, реалистичнее, но вариант просто в процентах - более играбельный, ведь игрок видит в инвентаре насколько его защищает данный костюм и эта цифра соответствует действительности.
Итак. Как сделать работу пулестойкости такой же, как остальные воздействия?
Для начала стоит вынести это дело под дефайн, дабы иметь возможность при необходимости включить/отключить данную правку.
Для этого, в файл build_config_defines.h(или в любой другой header) нужно добавить:
Код
#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;
}
{
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;
}
{
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
struct SBoneProtections;
#endif
Код
#ifndef FIRE_WOUND_HIT_FIXED
float HitThruArmour (float hit_power, s16 element, float AP);
#endif
float HitThruArmour (float hit_power, s16 element, float AP);
#endif
Код
#ifndef FIRE_WOUND_HIT_FIXED
SBoneProtections* m_boneProtection;
#endif
SBoneProtections* m_boneProtection;
#endif
Код
#ifndef FIRE_WOUND_HIT_FIXED
virtual BOOL BonePassBullet (int boneID);
#endif
virtual BOOL BonePassBullet (int boneID);
#endif
2. CustomOutfit.cpp
Код
#ifndef FIRE_WOUND_HIT_FIXED
#include "BoneProtections.h"
#endif
#include "BoneProtections.h"
#endif
Код
#ifndef FIRE_WOUND_HIT_FIXED
m_boneProtection = xr_new<SBoneProtections>();
#endif
m_boneProtection = xr_new<SBoneProtections>();
#endif
Код
#ifndef FIRE_WOUND_HIT_FIXED
xr_delete(m_boneProtection);
#endif
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
}
{
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
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
if(pSettings->line_exist(cNameSect(),"bones_koeff_protection"))
m_boneProtection->reload( pSettings->r_string(cNameSect(),"bones_koeff_protection"), smart_cast<CKinematics*>(pActor->Visual()) );
#endif
Вот и всё, если есть какие-то вопросы - пишите, возможно, я забыл что-то указать.
SHoC. Новые параметры для бронежилетов
В 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 );
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_additional_weight;
float m_additional_weight2;
добавим:
Код
float m_fHealthRestoreSpeed;
float m_fRadiationRestoreSpeed;
float m_fSatietyRestoreSpeed;
float m_fPowerRestoreSpeed;
float m_fBleedingRestoreSpeed;
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;
}
{
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
}
}
#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 ();
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 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 ();
virtual void UpdtateOutfitInSlot ();
SHoC. Создание нового индикатора на худе
Все мы с Вами знаем, что в ЗП добавлен ряд интересных и полезных индикаторов на худе. На самом деле добавить их легко. Разберем на примере индикатора поломки брони.
Для начала, в движке 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
////////////////////////////////////////////////////////////////////////////
// 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;
}
// 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);
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);
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");
}
}
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"
"ui_inGame2_circle_Armorbroken_yellow"
"ui_inGame2_circle_Armorbroken_red"
На этом всё . Собственно у меня по такой схеме сделаны все подобные иконки, потому что система ТЧ показалась мне неоправданно сложной и громоздкой. Ну, быть может, это на мой дилетантский взгляд.
P.S. Конечно, лучше совместить прогресс бар, который показывает состояние брони и этот индикатор. Чтобы два раза не дергать состояние костюма на апдейте. Но я не стал усложнять урок)
SHoC. Назначение скриптам горячих клавиш, изменяемых в главном меню.
Думаю из названия всё понятно. Создадим кнопку, которую можно будет легко изменять в опциях, а при нажатии будет вызываться функция из скрипта.
В файле: 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"
#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;
Добавим:{
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;
{
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")
[
.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>
Теперь в 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
function clock_key() -- вызывается из движка при нажатии кнопки часов
--действия при нажатии кнопки
end
На этом всё. Правда я не проверял, что будет, если скрипта или функции не существует.
Функции для работы с временем
xrGame:
level_script.cpp:
В началo файла, перед:
добавим инклуды:
Потом после:
добавить:
и в этом же файле, после:
Теперь в файле alife_time_manager.h, после:
добавим:
В файле alife_time_manager_inline.h добавим в конце:
XR_3DA:
Environment.h:
после:
Environment.cpp:
перед:
добавим:
level_script.cpp:
В началo файла, перед:
Код
using namespace luabind;
добавим инклуды:
Код
#include "alife_time_manager.h"
#include "game_sv_single.h"
#include "alife_simulator.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;
}
{
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);
}
}
{
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;
}
{
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);
};
{
fGameTime = NormalizeTime(fGameTime + game_time);
};
1.5.10
Stalker_Monolit:
Купол над актером
Код
r2_sun_depth_near_scale = 1.00001f но разрабы наверно забыли что диапазон стоит от 0,5 до 1,5 думаю что ставить 1.00001f было бесполезно если переключение идет только 0.5-1.0-1.5
r2_sun_depth_near_bias = 0.00005f диапазон -0.5 до 0,5
исправил на
r2_sun_depth_near_scale = 1.f я поставил средние значения
r2_sun_depth_near_bias = 0.f я поставил средние значения
r2_sun_depth_near_bias = 0.00005f диапазон -0.5 до 0,5
исправил на
r2_sun_depth_near_scale = 1.f я поставил средние значения
r2_sun_depth_near_bias = 0.f я поставил средние значения
Max Warlock:
Изменение кол-ва отображаемых группировок в PDA
UIRankingWnd.h:
Параметр max_factions
Forser:
Исправляем инвизибл кровососа
Привести метод "Hit" в файле Bloodsucker.cpp к следующему виду:
Код
void CAI_Bloodsucker::Hit(SHit* pHDS)
{
if (!collision_hit_off)
inherited::Hit(pHDS);
}
{
if (!collision_hit_off)
inherited::Hit(pHDS);
}
1.6.02
Stalker_Monolit:
Купол над актером
Код
r2_sun_depth_near_scale = 1.00001f но разрабы наверно забыли что диапазон стоит от 0,5 до 1,5 думаю что ставить 1.00001f было бесполезно если переключение идет только 0.5-1.0-1.5
r2_sun_depth_near_bias = 0.00005f диапазон -0.5 до 0,5
исправил на
r2_sun_depth_near_scale = 1.f я поставил средние значения
r2_sun_depth_near_bias = 0.f я поставил средние значения
r2_sun_depth_near_bias = 0.00005f диапазон -0.5 до 0,5
исправил на
r2_sun_depth_near_scale = 1.f я поставил средние значения
r2_sun_depth_near_bias = 0.f я поставил средние значения
Shoker:
Проблемы динамических моделей и их решение
Фикс сохранения клиентских объектов
В файле xrGame\Level_network.cpp в начале функции
void CLevel::ClientSend()
Перед проверкой GameID() == eGameIDSingle нужно поставить отрицание:
Код
if (GameID() != eGameIDSingle && OnClient())
Тогда при сохранении игры для всех клиентских объектов будет вызываться net_Export (клиентские объекты будут синхронизироваться с серверным чётко при сохранении)
Более подробно о ошибке
Из за этой опечатки в оригинале есть баг, что при сохранении игры в серверный объект попадают данные клиентского объекта, устаревшие на несколько апдейтов - именно в момент сохранения синхронизации не происходит. (Например если убрать в оружии сохранение патронов у клиентского объекта в CWeaponMagazined::save, оставив сохранение патронов только у серверного объекта - то если выстрелить и сразу сохраниться -> при загрузке число патронов будет прежним)
Кто хочет понять природу бага - надо начинать смотреть с функции void CALifeStorageManager::prepare_objects_for_save() в alife_storage_manager.cpp. Она вызывается во время сохранения и в свою очередь вызывает CLevel::ClientSend(), однако там из за опечатки происходит проверка на пропускную способность сети net_HasBandwith() (для МП), которая в 99% возвращает false.
Баг не то, чтобы критичный но всё же серьёзный.
SkyLoader:
Вертикальная синхронизация (дополнение для r3/r4)
dxRenderDeviceRender.cpp:
Код
HW.m_pSwapChain->Present( 0, 0);
Добавляем проверку на включение вертикальной синхронизации:
Код
HW.m_pSwapChain->Present(psDeviceFlags.test(rsVSync) ? 1 : 0, 0);
Очень странный вылет
Данная проблема была в свое в ЛА еще в 2013 году, и я очень долго не мог найти причину вылета, так как вылет каждый раз ссылался на разные некорректные по его мнению объекты. Причем возникал не всегда и можно было его поймать только при выходе из игры. Спустя несколько месяцев чудом проблема была найдена. Оказывается, проблема была в лампочках, которые включались и выключались скриптово. Игра ловила крэш при 255 и более включении и выключении лампочки. Фиксится в HangingLamp.cpp в строках:
Код
if(!PPhysicsShell())//if we have physiccs_shell it will call processing deactivate when disable
processing_deactivate ();
processing_deactivate ();
Удаляем if(!PPhysicsShell()), теперь processing_deactivate(); будет срабатывать всегда, как и должен, и мы можем включать и выключать лампочки сколько угодно раз.
Эффектор перезарядки срабатывает не каждый раз
Эффектор перезарядки срабатывает не каждый раз [ЗП]
Ну тут всё просто, просто перезапускаем активный эффектор. Заходим в player_hud.cpp, находим строки 380-381:
Код
if(NULL==ec)
{
{
Эти две строки с закрывающейся скобкой удаляем. Вместо этого вставляем:
Код
if(ec)
current_actor->Cameras().RemoveCamEffector(eCEWeaponAction);
current_actor->Cameras().RemoveCamEffector(eCEWeaponAction);
Теперь эффектор работает при каждой перезарядке.
Растянутое небо
Код
[b]dxEnvironmentRender.cpp:[/b]
static Fvector3 hbox_verts[24] =
{
{-1.f, -1.f, -1.f}, {-1.f, -1.01f, -1.f}, // down
{ 1.f, -1.f, -1.f}, { 1.f, -1.01f, -1.f}, // down
{-1.f, -1.f, 1.f}, {-1.f, -1.01f, 1.f}, // down
{ 1.f, -1.f, 1.f}, { 1.f, -1.01f, 1.f}, // down
{-1.f, 1.f, -1.f}, {-1.f, 1.f, -1.f},
{ 1.f, 1.f, -1.f}, { 1.f, 1.f, -1.f},
{-1.f, 1.f, 1.f}, {-1.f, 1.f, 1.f},
{ 1.f, 1.f, 1.f}, { 1.f, 1.f, 1.f},
{-1.f, 0.f, -1.f}, {-1.f, -1.f, -1.f}, // half
{ 1.f, 0.f, -1.f}, { 1.f, -1.f, -1.f}, // half
{ 1.f, 0.f, 1.f}, { 1.f, -1.f, 1.f}, // half
{-1.f, 0.f, 1.f}, {-1.f, -1.f, 1.f} // half
};
static Fvector3 hbox_verts[24] =
{
{-1.f, -1.f, -1.f}, {-1.f, -1.01f, -1.f}, // down
{ 1.f, -1.f, -1.f}, { 1.f, -1.01f, -1.f}, // down
{-1.f, -1.f, 1.f}, {-1.f, -1.01f, 1.f}, // down
{ 1.f, -1.f, 1.f}, { 1.f, -1.01f, 1.f}, // down
{-1.f, 1.f, -1.f}, {-1.f, 1.f, -1.f},
{ 1.f, 1.f, -1.f}, { 1.f, 1.f, -1.f},
{-1.f, 1.f, 1.f}, {-1.f, 1.f, 1.f},
{ 1.f, 1.f, 1.f}, { 1.f, 1.f, 1.f},
{-1.f, 0.f, -1.f}, {-1.f, -1.f, -1.f}, // half
{ 1.f, 0.f, -1.f}, { 1.f, -1.f, -1.f}, // half
{ 1.f, 0.f, 1.f}, { 1.f, -1.f, 1.f}, // half
{-1.f, 0.f, 1.f}, {-1.f, -1.f, 1.f} // half
};
Shredder:
Воздействие аномалий на НПС
В классе CSpaceRestrictor в методе net_Spawn убираем строчку
Код
spatial.type &= ~STYPE_VISIBLEFORAI
Может, конечно, я тут Америку не открыл, но мне никто так и не подсказал.
Правда после включения наблюдается интересная картина. Если сталкер после первого хита выжил, он чуть отходит назад и начинает кидать болты )) Правда не совсем туда, куда надо. Да и выглядит это кривовато, но думаю эту схему можно отключить и подключить скриптовую, например из amk для ТЧ
Forser:
Размытые шрифты на dx11
В dxFontRender.cpp ищем:
Код
#ifndef USE_DX10
//Make half pixel offset for 1 to 1 mapping
tu +=( 0.5f / owner.vTS.x );
tv +=( 0.5f / owner.vTS.y );
#endif // USE_DX10
//Make half pixel offset for 1 to 1 mapping
tu +=( 0.5f / owner.vTS.x );
tv +=( 0.5f / owner.vTS.y );
#endif // USE_DX10
И модифицируем проверку:
Код
#if !defined(USE_DX10) && !defined(USE_DX11)
KD & nikita_nz1986 & Forser:
Тень от травы
Так значит:Тень травы в файле DetailManager_VS.cpp(r2 only, совместимо с 1.0007) после
Код
RCache.stat.r.s_details.add (dwCNT_verts);
}
}
добавить:
Код
// KD: we must not clear vis on r2 since we want details shadows
#if RENDER==R_R2
if ((ps_r2_ls_flags.test(R2FLAG_SUN_DETAILS) && (RImplementation.PHASE_SMAP == RImplementation.phase)) // phase smap with shadows
|| (ps_r2_ls_flags.test(R2FLAG_SUN_DETAILS) && (RImplementation.PHASE_NORMAL == RImplementation.phase) && (!RImplementation.is_sun())) // phase normal with shadows without sun
|| (!ps_r2_ls_flags.test(R2FLAG_SUN_DETAILS) && (RImplementation.PHASE_NORMAL == RImplementation.phase))) // phase normal without shadows
#endif
#if RENDER==R_R2
if ((ps_r2_ls_flags.test(R2FLAG_SUN_DETAILS) && (RImplementation.PHASE_SMAP == RImplementation.phase)) // phase smap with shadows
|| (ps_r2_ls_flags.test(R2FLAG_SUN_DETAILS) && (RImplementation.PHASE_NORMAL == RImplementation.phase) && (!RImplementation.is_sun())) // phase normal with shadows without sun
|| (!ps_r2_ls_flags.test(R2FLAG_SUN_DETAILS) && (RImplementation.PHASE_NORMAL == RImplementation.phase))) // phase normal without shadows
#endif
И аналогично в dx10DetailManager_VS.cpp в метод hw_Render_dump без проверки макроса RENDER
Далее в r2.cpp, а также в r3.cpp и в r4.cpp добавить функцию
Код
bool CRender::is_sun()
{
if (o.sunstatic) return FALSE;
Fcolor sun_color = ((light*)Lights.sun_adapted._get())->color;
return (ps_r2_ls_flags.test(R2FLAG_SUN) && (u_diffuse2s(sun_color.r, sun_color.g, sun_color.B )>EPS));
}
{
if (o.sunstatic) return FALSE;
Fcolor sun_color = ((light*)Lights.sun_adapted._get())->color;
return (ps_r2_ls_flags.test(R2FLAG_SUN) && (u_diffuse2s(sun_color.r, sun_color.g, sun_color.B )>EPS));
}
Затем в r2.h, а также в r3.h и в r4.h добавить идентификатор:
Код
bool is_sun();
Alundaio:
Исправление недоступности вызова inherited::inherited::schedule_Update(dt)
CustomMonster.h:
Изменяем уровень доступа для inherited
Код
protected:
typedef CEntityAlive inherited;
typedef CEntityAlive inherited;
entity_alive.h Добавляем protected перед определением inherited:
Код
protected:
typedef CEntity inherited;
private:
typedef CEntity inherited;
private:
Исправление недоступности вызова inherited::inherited::ProcessCam(info)
ActorEffector.h:Добавляем protected перед определением inherited:
Код
protected:
typedef CCameraManager inherited;
private:
Исправление Death callback для GameObject
DestroyablePhysicsObject.cpp:
Находим:
Код
callback(GameObject::eDeath)(lua_game_object(),who_object ? who_object : 0);
И приводим к след.виду:
Код
callback(GameObject::eDeath)(lua_game_object(),who_object ? who_object->lua_game_object() : 0);
Nanobot:
Исправление explode() для гранат
Находим файл script_game_object2.cpp и правим метод void CScriptGameObject::explode(u32 level_time)
Заменяем это:
На это:
Заменяем это:
Код
explosive->SetInitiator(object().ID());
На это:
Код
explosive->SetInitiator(explosive->Initiator()); // устанавливаем инициатор только если он неизвестен.
Не правильно работает гранатная прокрутка?
Код
bool CGrenade::Action(s32 cmd, u32 flags)
{
if(inherited::Action(cmd, flags)) return true;
switch(cmd)
{
//переключение типа гранаты
case kWPN_NEXT:
{
if(flags&CMD_START)
{
if(m_pCurrentInventory)
{
// (c) NanoBot
xr_vector<shared_str> types_sect_grn; // текущий список секций гранат
// Находим список секций гранат разных типов в активе
// в m_belt или m_ruck нет гранаты которую актор держит в руках, т.е. this
types_sect_grn.push_back(this->cNameSect());
int count_types = 1; // текущие количество типов гранат в активе
//GRENADE_FROM_BELT
TIItemContainer::iterator it, it_e;
if(Belt())
{
it = m_pCurrentInventory->m_belt.begin();
it_e = m_pCurrentInventory->m_belt.end();
}
else
{
it = m_pCurrentInventory->m_ruck.begin();
it_e = m_pCurrentInventory->m_ruck.end();
}
for(;it!=it_e;++it)
{
CGrenade *pGrenade = smart_cast<CGrenade*>(*it);
if(pGrenade)
{
// составляем список типов гранат (с) НаноБот
xr_vector<shared_str>::const_iterator I = types_sect_grn.begin();
xr_vector<shared_str>::const_iterator E = types_sect_grn.end();
bool new_type = true;
for (; I != E; ++I)
{
if(!xr_strcmp(pGrenade->cNameSect(), *I)) // если совпадают
new_type = false;
}
if(new_type) // новый тип гранаты?, добавляем
{
types_sect_grn.push_back(pGrenade->cNameSect());
count_types++;
}
}
}
// Если типов больше 1 то, сортируем список по алфавиту
// и находим номер текущей гранаты в списке.
if(count_types>1)
{
int curr_num = 0; // номер типа текущей гранаты
std::sort(types_sect_grn.begin(), types_sect_grn.end());
xr_vector<shared_str>::const_iterator I = types_sect_grn.begin();
xr_vector<shared_str>::const_iterator E = types_sect_grn.end();
for (; I != E; ++I)
{
if(!xr_strcmp(this->cNameSect(), *I)) // если совпадают
break;
curr_num++;
}
int next_num = curr_num+1; // номер секции следующей гранаты
if(next_num>=count_types) next_num = 0;
shared_str sect_next_grn = types_sect_grn[next_num]; // секция следущей гранаты
// Ищем в активе гранату с секцией следущего типа
//GRENADE_FROM_BELT
TIItemContainer::iterator it, it_e;
if(Belt())
{
it = m_pCurrentInventory->m_belt.begin();
it_e = m_pCurrentInventory->m_belt.end();
}
else
{
it = m_pCurrentInventory->m_ruck.begin();
it_e = m_pCurrentInventory->m_ruck.end();
}
for(;it!=it_e;++it)
{
CGrenade *pGrenade = smart_cast<CGrenade*>(*it);
if(pGrenade && !xr_strcmp(pGrenade->cNameSect(), sect_next_grn))
{
m_pCurrentInventory->Ruck(this);
m_pCurrentInventory->SetActiveSlot(NO_ACTIVE_SLOT);
m_pCurrentInventory->Slot(pGrenade);
//GRENADE_FROM_BELT
if(Belt())
m_pCurrentInventory->Belt(this); // текущую гранату, обратно в пояс.
return true;
}
}
}
return true;
}
}
return true;
};
}
return false;
}
{
if(inherited::Action(cmd, flags)) return true;
switch(cmd)
{
//переключение типа гранаты
case kWPN_NEXT:
{
if(flags&CMD_START)
{
if(m_pCurrentInventory)
{
// (c) NanoBot
xr_vector<shared_str> types_sect_grn; // текущий список секций гранат
// Находим список секций гранат разных типов в активе
// в m_belt или m_ruck нет гранаты которую актор держит в руках, т.е. this
types_sect_grn.push_back(this->cNameSect());
int count_types = 1; // текущие количество типов гранат в активе
//GRENADE_FROM_BELT
TIItemContainer::iterator it, it_e;
if(Belt())
{
it = m_pCurrentInventory->m_belt.begin();
it_e = m_pCurrentInventory->m_belt.end();
}
else
{
it = m_pCurrentInventory->m_ruck.begin();
it_e = m_pCurrentInventory->m_ruck.end();
}
for(;it!=it_e;++it)
{
CGrenade *pGrenade = smart_cast<CGrenade*>(*it);
if(pGrenade)
{
// составляем список типов гранат (с) НаноБот
xr_vector<shared_str>::const_iterator I = types_sect_grn.begin();
xr_vector<shared_str>::const_iterator E = types_sect_grn.end();
bool new_type = true;
for (; I != E; ++I)
{
if(!xr_strcmp(pGrenade->cNameSect(), *I)) // если совпадают
new_type = false;
}
if(new_type) // новый тип гранаты?, добавляем
{
types_sect_grn.push_back(pGrenade->cNameSect());
count_types++;
}
}
}
// Если типов больше 1 то, сортируем список по алфавиту
// и находим номер текущей гранаты в списке.
if(count_types>1)
{
int curr_num = 0; // номер типа текущей гранаты
std::sort(types_sect_grn.begin(), types_sect_grn.end());
xr_vector<shared_str>::const_iterator I = types_sect_grn.begin();
xr_vector<shared_str>::const_iterator E = types_sect_grn.end();
for (; I != E; ++I)
{
if(!xr_strcmp(this->cNameSect(), *I)) // если совпадают
break;
curr_num++;
}
int next_num = curr_num+1; // номер секции следующей гранаты
if(next_num>=count_types) next_num = 0;
shared_str sect_next_grn = types_sect_grn[next_num]; // секция следущей гранаты
// Ищем в активе гранату с секцией следущего типа
//GRENADE_FROM_BELT
TIItemContainer::iterator it, it_e;
if(Belt())
{
it = m_pCurrentInventory->m_belt.begin();
it_e = m_pCurrentInventory->m_belt.end();
}
else
{
it = m_pCurrentInventory->m_ruck.begin();
it_e = m_pCurrentInventory->m_ruck.end();
}
for(;it!=it_e;++it)
{
CGrenade *pGrenade = smart_cast<CGrenade*>(*it);
if(pGrenade && !xr_strcmp(pGrenade->cNameSect(), sect_next_grn))
{
m_pCurrentInventory->Ruck(this);
m_pCurrentInventory->SetActiveSlot(NO_ACTIVE_SLOT);
m_pCurrentInventory->Slot(pGrenade);
//GRENADE_FROM_BELT
if(Belt())
m_pCurrentInventory->Belt(this); // текущую гранату, обратно в пояс.
return true;
}
}
}
return true;
}
}
return true;
};
}
return false;
}
Гранаты прокручиваются в алфавитном порядке, по имени секции.
использование гранат из рюкзака, или из пояса, в зависимости от настройки конфига
Код
void CGrenade::PutNextToSlot()
{
if (OnClient()) return;
VERIFY (!getDestroy());
//выкинуть гранату из инвентаря
if (m_pCurrentInventory)
{
NET_Packet P;
m_pCurrentInventory->Ruck (this);
//GRENADE_FROM_BELT
this->u_EventGen (P, (Belt() ? GEG_PLAYER_ITEM2BELT : GEG_PLAYER_ITEM2RUCK), this->H_Parent()->ID());
P.w_u16 (this->ID());
this->u_EventSend (P);
//GRENADE_FROM_BELT
CGrenade *pNext = smart_cast<CGrenade*>( m_pCurrentInventory->Same(this, !Belt()) );
if(!pNext)
pNext = smart_cast<CGrenade*>( m_pCurrentInventory->SameSlot(GRENADE_SLOT, this, !Belt()));
VERIFY (pNext != this);
if(pNext && m_pCurrentInventory->Slot(pNext) )
{
pNext->u_EventGen (P, GEG_PLAYER_ITEM2SLOT, pNext->H_Parent()->ID());
P.w_u16 (pNext->ID());
pNext->u_EventSend (P);
m_pCurrentInventory->SetActiveSlot(pNext->GetSlot());
}
///// m_thrown = false;
}
}
{
if (OnClient()) return;
VERIFY (!getDestroy());
//выкинуть гранату из инвентаря
if (m_pCurrentInventory)
{
NET_Packet P;
m_pCurrentInventory->Ruck (this);
//GRENADE_FROM_BELT
this->u_EventGen (P, (Belt() ? GEG_PLAYER_ITEM2BELT : GEG_PLAYER_ITEM2RUCK), this->H_Parent()->ID());
P.w_u16 (this->ID());
this->u_EventSend (P);
//GRENADE_FROM_BELT
CGrenade *pNext = smart_cast<CGrenade*>( m_pCurrentInventory->Same(this, !Belt()) );
if(!pNext)
pNext = smart_cast<CGrenade*>( m_pCurrentInventory->SameSlot(GRENADE_SLOT, this, !Belt()));
VERIFY (pNext != this);
if(pNext && m_pCurrentInventory->Slot(pNext) )
{
pNext->u_EventGen (P, GEG_PLAYER_ITEM2SLOT, pNext->H_Parent()->ID());
P.w_u16 (pNext->ID());
pNext->u_EventSend (P);
m_pCurrentInventory->SetActiveSlot(pNext->GetSlot());
}
///// m_thrown = false;
}
}
Модернизация GetCurrentFireMode для ухода от CWeaponCustomPistol
Код
virtual int GetCurrentFireMode() { return m_bHasDifferentFireModes ? m_aFireModes[m_iCurFireMode] : 1; };
Giperion, Forser:
Утечка памяти
Исходник: src/Layers/xrRenderDX10/dx10ResourceManager_Resources.cpp
Код
IReader* R = FS.r_open(cname);
// TODO: DX10: HACK: Implement all shaders. Remove this for PS
if (!R)
{
string1024 tmp;
// TODO: HACK: Test failure
//Memory.mem_compact();
xr_sprintf (tmp, "DX10: %s is missing. Replace with stub_default.ps", cname);
Msg (tmp);
strconcat (sizeof(cname), cname,GlobalEnv.Render->getShaderPath(),"stub_default",".ps");
FS.update_path (cname, "$game_shaders$", cname);
R = FS.r_open(cname);
}
IReader* file = FS.r_open(cname);
R_ASSERT2 ( file, cname );
u32 const size = file->length();
// TODO: DX10: HACK: Implement all shaders. Remove this for PS
if (!R)
{
string1024 tmp;
// TODO: HACK: Test failure
//Memory.mem_compact();
xr_sprintf (tmp, "DX10: %s is missing. Replace with stub_default.ps", cname);
Msg (tmp);
strconcat (sizeof(cname), cname,GlobalEnv.Render->getShaderPath(),"stub_default",".ps");
FS.update_path (cname, "$game_shaders$", cname);
R = FS.r_open(cname);
}
IReader* file = FS.r_open(cname);
R_ASSERT2 ( file, cname );
u32 const size = file->length();
IReader* R - незакрытый объект файла. Который еще и не используется дальше, ведь его подменяет file, который потом успешно закрывается.
Код
IReader* file = FS.r_open(cname);
// TODO: DX10: HACK: Implement all shaders. Remove this for PS
if (!file)
{
string1024 tmp;
//TODO: HACK: Test failure
xr_sprintf(tmp, "DX10: %s is missing. Replace with stub_default.ps", cname);
Msg(tmp);
strconcat(sizeof(cname), cname,GlobalEnv.Render->getShaderPath(),"stub_default",".ps");
FS.update_path(cname, "$game_shaders$", cname);
file = FS.r_open(cname);
}
R_ASSERT2 (file, cname);
u32 const size = file->length();
// TODO: DX10: HACK: Implement all shaders. Remove this for PS
if (!file)
{
string1024 tmp;
//TODO: HACK: Test failure
xr_sprintf(tmp, "DX10: %s is missing. Replace with stub_default.ps", cname);
Msg(tmp);
strconcat(sizeof(cname), cname,GlobalEnv.Render->getShaderPath(),"stub_default",".ps");
FS.update_path(cname, "$game_shaders$", cname);
file = FS.r_open(cname);
}
R_ASSERT2 (file, cname);
u32 const size = file->length();
Утечка обнаружена Giperion'ом.
qweasdd136963:
Глитч камеры при перезарядке
Правка позиции ведения огня для актора
Правка позиции ведения огня для актора, т.к. она идет от центра камеры
Файл: Actor_Weapon.cpp
Переработка кода: ForserX
Файл: Actor_Weapon.cpp
Код
void CActor::g_fireParams (const CHudItem* pHudItem, Fvector &fire_pos, Fvector &fire_dir)
{
CWeapon *weapon = smart_cast<CWeapon*>(inventory().ActiveItem());
if(weapon)
{
fire_pos = weapon->get_LastFP();
}
else
{
const CMissile *pMissile = smart_cast <const CMissile*> (pHudItem);
if (pMissile)
{
fire_pos = Actor()->Position();
fire_pos.y += 1.24f;
}
}
fire_dir = Cameras().Direction();
}
{
CWeapon *weapon = smart_cast<CWeapon*>(inventory().ActiveItem());
if(weapon)
{
fire_pos = weapon->get_LastFP();
}
else
{
const CMissile *pMissile = smart_cast <const CMissile*> (pHudItem);
if (pMissile)
{
fire_pos = Actor()->Position();
fire_pos.y += 1.24f;
}
}
fire_dir = Cameras().Direction();
}
Переработка кода: ForserX
P.S. Местами код правился, для сокращения объёма этого сообщения, мог подтереть что-то важное. При нахождении ошибок прошу сообщать.