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

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

 
 

Policai У контролёра нету функции стрельбы, я так хотел, перестрелка с ним, тот с РПК, а после того, как добьешь- он прет в бой руками) Прикольно бы смотрелось

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

Волк волку- волк, человек человеку- собутыльник.

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


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

Ayden Тогда спавните просто зомбированного НПС, добавляете ему ауру при помощи скриптов (как это сделано можно посмотреть в моде Отступник), а для реализации боя руками видимо придется заспавнить вместо НПС, какого то мутанта с визуалом зомбированного, но это будет криво смотреться по многим причинам.

  • Жму руку 1

Путь во мгле. Связь времен.
"Он ловко выхватил из-под себя табуретку и очень метко и сильно бросил ее в докладчика..." (Чугунный всадник)

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


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

В sr_camp добавлялась новая секция, чтобы НПС проигрывали легенды у костра. Игра запускается нормально, но после некоторого времени в лагере, вылетает с ошибкой:

[LUA] Error: [string "sound_theme"]:225: bad argument #1 to 'gsub' (string expected, got nil)

В чем может быть проблема? 

Прикладываю свой sr_camp.

Спойлер

local npc_role = {noone=0, listener=1, director=2}
CAMP_SECTION = "camp"

--------------------------------------------------------------------------------
-- Precondition functions
--------------------------------------------------------------------------------
function sr_camp_idle_precondition(camp)
    return true
end
function sr_camp_harmonica_precondition(camp)
    --printf("camp harmonica precondition")
    if(#camp.harmonica_table>0) then
        local n = 0
        for k,v in pairs(camp.npc) do
            n = n + 1
        end
        if(n>1) then
            for k,v in pairs(camp.npc) do                
                local scheme = db.storage[k] and db.storage[k].active_scheme and  db.storage[k][db.storage[k].active_scheme]
                
                local npc = db.storage[k] and db.storage[k].object

                --printf("    is director %s", tostring(v.harmonica==npc_role.director))
                --printf("    base_action %s", tostring(scheme.base_action) )
                --printf("    description %s", tostring(scheme.description) )
                if(v.harmonica==npc_role.director) and (scheme ~= nil and scheme.base_action == scheme.description) and npc ~= nil and not xr_meet.is_meet(npc) then
                --if(v.harmonica==npc_role.director) then
                    --printf("    return true [%s]", k)
                    return true
                end
            end
        end
    end
    --printf("    return false")
    return false
end
function sr_camp_guitar_precondition(camp)
    --printf("camp guitar precondition")
    if(#camp.guitar_table>0) then
        local n = 0
        for k,v in pairs(camp.npc) do
            n = n + 1
        end
        if(n>1) then
            for k,v in pairs(camp.npc) do
                local scheme = db.storage[k] and db.storage[k].active_scheme and db.storage[k][db.storage[k].active_scheme]
                
                local npc = db.storage[k] and db.storage[k].object
                --printf("    is director %s", tostring(v.guitar==npc_role.director))
                --printf("    base_action %s", tostring(scheme.base_action) )
                --printf("    description %s", tostring(scheme.description) )
                
                if(v.guitar==npc_role.director) and (scheme ~= nil and scheme.base_action == scheme.description) and npc ~= nil and not xr_meet.is_meet(npc) then
                --if(v.guitar==npc_role.director) then
                    --printf("    return true [%s]", k)
                    return true
                end
            end
        end
    end
    --printf("    return false")
    return false
end
function sr_camp_story_precondition(camp)
    if(#camp.story_table>0) then
        local n = 0
        for k,v in pairs(camp.npc) do
        
            local npc = db.storage[k] and db.storage[k].object
            if npc ~= nil and not xr_meet.is_meet(npc) then
                n = n + 1
            end
        end
        if(n>1) then
            return true
        end
    end
    return false
end
function sr_camp_legend_precondition(camp)
    if(#camp.legend_table>0) then
        local n = 0
        for k,v in pairs(camp.npc) do
            n = n + 1
        end
        if(n>1) then
            for k,v in pairs(camp.npc) do
                local scheme = db.storage[k] and db.storage[k].active_scheme and db.storage[k][db.storage[k].active_scheme]
                
                local npc = db.storage[k] and db.storage[k].object
                
                if(v.legend==npc_role.director) and (scheme ~= nil and scheme.base_action == scheme.description) and npc ~= nil and not xr_meet.is_meet(npc) then
                    return true
                end
            end
        end
    end
    return false
end
--------------------------------------------------------------------------------
-- Класс CCampManager
--------------------------------------------------------------------------------
-- Кемп содержит свои действия в терминах состояния кемпа (рассказ истории, музыка, ожидание), а не в терминах анимаций персонажей
-- Кемп возвращает свое состояние, а решение какую именно анимацию играть принимает финальная схема, например xr_animpoint
class "CCampManager"
function CCampManager:__init(object, ini)
    self.object = object
    self.ini = ini
    local stories = utils.cfg_get_string(ini, CAMP_SECTION, "stories", nil, false, "", "test_story")
        self.story_table = parse_names(stories)
    local guitars = utils.cfg_get_string(ini, CAMP_SECTION, "guitar_themes", nil, false, "", "test_guitar")
        self.guitar_table = parse_names(guitars)
    local harmonicas = utils.cfg_get_string(ini, CAMP_SECTION, "harmonica_themes", nil, false, "", "test_harmonica")
        self.harmonica_table = parse_names(harmonicas)
    local legends = utils.cfg_get_string(ini, CAMP_SECTION, "legends_and_stories", nil, false, "", "legend001")
        self.legend_table = parse_names(legends)
    self.npc = {}
    self.schemes = {}
    -- Хранилище для режиссера лагеря. Режиссерем является сталкер, затеявший необычное поведение
    self.director = nil
    -- Текущее состояние
    self.active_state = "idle"
    --' Для теста создаем объект истории
    self.sound_manager = sound_manager.get_sound_manager(CAMP_SECTION..self.object:id())
    self.sound_manager_started = true
    -- Состояния кемпа
    self.states = {
                    idle = {
                                director_state = nil,
                                general_state = "idle",
                                min_time = 30000,
                                max_time = 40000,
                                timeout = 0,
                                transitions = {harmonica = 20, guitar = 20, story = 30, legend = 30},
                                precondition = sr_camp_idle_precondition
                             },
                    harmonica = {
                                director_state = "play_harmonica",
                                general_state = "listen",
                                min_time = 10000,
                                max_time = 11000,
                                timeout = 3000,
                                transitions = {idle = 100, harmonica = 0, guitar = 0, story = 0, legend = 0},
                                precondition = sr_camp_harmonica_precondition
                                },
                    guitar = {
                                director_state = "play_guitar",
                                general_state = "listen",
                                min_time = 10000,
                                max_time = 11000,
                                timeout = 4500,
                                transitions = {idle = 100, harmonica = 0, guitar = 0, story = 0, legend = 0},
                                precondition = sr_camp_guitar_precondition
                             },
                    legend = {
                                director_state = "legend",
                                general_state = "listen",
                                min_time = 10000,
                                max_time = 22000,
                                timeout = 0,
                                transitions = {idle = 100, harmonica = 0, guitar = 0, story = 0, legend = 0},
                                precondition = sr_camp_legend_precondition
                             },
                    story = {
                                director_state = "tell",
                                general_state = "listen",
                                min_time = 10000,
                                max_time = 11000,
                                timeout = 0,
                                transitions = {idle = 100, harmonica = 0, guitar = 0, story = 0, legend = 0},
                                precondition = sr_camp_story_precondition
                            },
                    }
    self.active_state_time = 0
    self.timeout = 0
    self.idle_talker = nil
end
-- Переключает состояния кемпа
function CCampManager:update()

    -- Если саундменеджер говорит какую то историю - ждем окончания.
    if not self.sound_manager:is_finished() then
        self.sound_manager:update()
        return
    end

    if self.sound_manager_started == false then
        return
    end

    -- Если кто-то говорит айдловую фразу, ждем пока он договорит.
    if(self.idle_talker~=nil) then
        if(xr_sound.sound_table[self.idle_talker]) then
            return
        else
            self.idle_talker = nil
        end
    end

    -- Выбор состояния кемпа. Тут решается что делать дальше.
    --printf("camp time %s", tostring(self.active_state_time - time_global()))
    if(self.active_state_time < time_global()) then
        self:set_next_state()
        if self:get_director() == false then
        
            self.active_state = "idle"
            for k,v in pairs(self.npc) do
                v.state = self.active_state
            end
        
            --self:set_next_state()
        end

        self.sound_manager_started = false
        for k,v in pairs(self.npc) do
            if(db.storage[k]) then
                xr_logic.issue_event(db.storage[k].object, db.storage[k][db.storage[k].active_scheme], "update")
            end

            local meet = db.storage[k] and db.storage[k].meet and db.storage[k].meet.meet_manager
            if meet then
                meet.npc_is_camp_director = self.director == k
            end
        end
    end

    -- Выбираем новую тему для саундменеджера (Для гитары и гармошки нужно будет юзать другой метод, чтобы не начали играть звук до того, как достанут инструмент)
    if(self.timeout~=0) and (self.timeout<=time_global()) then
        self:set_story()
        self.timeout = 0
    end

    -- Болтовня в айдле
    if(self.active_state=="idle") then
        local npc_count = 0
        local talkers = {}
        for k,v in pairs(self.npc) do
            npc_count = npc_count + 1
            table.insert(talkers, k)
        end
        if(npc_count~=0) then
            self.idle_talker = talkers[math.random(#talkers)]
            xr_sound.set_sound_play(self.idle_talker, "state")
        end
    end


end
function CCampManager:set_next_state()
    --printf("CAMP set_next_state")
    local transitions = self.states[self.active_state].transitions
    local rnd = math.random(100)
    for k,v in pairs(transitions) do
        --printf("check %s value %s rnd %s", k, v, rnd )
        if(rnd<v) then
            if self.states[k].precondition(self) == true then
                self.active_state = k
                --printf("FOUND")
                break
            end
        else
            rnd = rnd - v
        end
    end
    --printf("active state %s", self.active_state)
    for k,v in pairs(self.npc) do
        v.state = self.active_state
    end
    self.active_state_time = time_global()+math.random(self.states[self.active_state].min_time, self.states[self.active_state].max_time)
    self.timeout = time_global()+self.states[self.active_state].timeout
end
function CCampManager:get_director()
    if(self.active_state=="idle") then
        self.director = nil
        return
    end
    local directors = {}
    local npc_count = 0
    for k,v in pairs(self.npc) do
        npc_count = npc_count + 1
        local storage = db.storage[k] 
        if storage ~= nil then
            local scheme = storage.active_scheme and storage[storage.active_scheme]
            local npc = storage.object
            if(v[self.active_state]==npc_role.director) and (scheme ~= nil and scheme.base_action == scheme.description) and not xr_meet.is_meet(npc) then
                table.insert(directors, k)
            end
        end
    end
    if(npc_count==0) then
        self.director = nil
        return
    end
    if(#directors<1) then
        return false
        --abort("There is no director for state [%s]. Camp [%s]!!!", self.active_state, self.object:name())
    elseif(#directors==1) then
        self.director = directors[1]
    else
        self.director = directors[math.random(#directors)]
    end
    --printf("camp director [%s]", self.director)
end
function CCampManager:set_story()
    if(self.active_state=="story") then
        self.sound_manager:set_storyteller(self.director)
        self.sound_manager:set_story(self.story_table[math.random(#self.story_table)])
        self.sound_manager_started = true
    elseif(self.active_state=="idle") then
        self.sound_manager_started = true
    end
end
-- Возвращает текущее действие кемпа
function CCampManager:get_camp_action(npc_id)
    if(npc_id==nil) then
        abort("Trying to use destroyed object!")
    end
    if(self.npc[npc_id]==nil) then
        return
    end
    return self.npc[npc_id].state, self.director==npc_id
end
-- Регистрит персонажа в кемп
function CCampManager:register_npc(npc_id)
--    local npc_id = npc:id()
    --printf("Register NPC to camp %s", self.object:id())
    self.npc[npc_id] = {state = self.active_state}
    db.storage[npc_id].registred_camp = self.object:id()

    -- При регистрации персонажа, проверяем какие роли он может выполнять.
    for k,v in pairs(self.states) do
        --printf("FIND NPC ROLE %s", npc_id)
        local role = self:get_npc_role(npc_id, k)
        if(role==npc_role.noone) then
            abort("Wrong role for npc[%s] with id[%d] in camp [%s]!!!", npc:name(), npc_id, self.object:name())
        end
        self.npc[npc_id][k] = role
    end
    self.sound_manager:register_npc(npc_id)

    xr_logic.issue_event(db.storage[npc_id].object, db.storage[npc_id][db.storage[npc_id].active_scheme], "update")
end
-- Убирает персонажа из кемпа
function CCampManager:unregister_npc(npc_id)
--    local npc_id = npc:id()
    -- Если удаляется режиссер лагеря
    if self.director == npc_id then
        self.sound_manager_started = false
        self.active_state_time = 0
        self.director = nil
        
        self.active_state = "idle"
        for k,v in pairs(self.npc) do
            v.state = self.active_state
        end        
    end


    db.storage[npc_id].registred_camp = nil
    self.npc[npc_id] = nil
    self.sound_manager:unregister_npc(npc_id)
end
function CCampManager:get_npc_role(npc_id, state)
    local scheme = db.storage[npc_id][db.storage[npc_id].active_scheme]
    if scheme == nil then
        return npc_role.noone
    end

    local npc_actions = scheme.approved_actions
    local descr = scheme.description
    if(state=="harmonica") or (state=="guitar") or (state=="legend") then
        descr = descr.."_"..state
        --printf("finding music director [%s]", descr) 
        for i = 1,#npc_actions do
            --printf("check action [%s]", npc_actions[i].name)
            if(npc_actions[i].name==descr) then
                --printf("DIRECTOR FOUND music")
                return npc_role.director
            end
        end
        return npc_role.listener
    elseif(state=="story") then
        for i = 1,#npc_actions do
            if(npc_actions[i].name==descr) or (npc_actions[i].name==descr.."_weapon") or (npc_actions[i].name==descr.."_s") or (npc_actions[i].name==descr.."_sk") or (npc_actions[i].name==descr.."_sa") or (npc_actions[i].name==descr.."_sr") or (npc_actions[i].name==descr.."_so2") or (npc_actions[i].name==descr.."_so3") then
                --printf("DIRECTOR FOUND story")
                return npc_role.director
            end
        end
        return npc_role.listener
    elseif(state=="idle") then
        return npc_role.listener
    end
    return npc_role.noone
end
--------------------------------------------------------------------------------
function get_current_camp(position)
    for k,v in pairs(bind_camp.camps) do
        if v.object:inside(position) then
            return v.camp
        end
    end
    return nil
end

function start_guitar(npc)
    local camp_id = db.storage[npc:id()].registred_camp
    if camp_id == nil then
        return    
        --abort("trying to play guitar without camp [%s]",tostring(npc:name()))
    end
    local camp = bind_camp.camps[camp_id].camp

    camp.sound_manager:set_storyteller(camp.director)
    camp.sound_manager:set_story(camp.guitar_table[math.random(#camp.guitar_table)])
    camp.sound_manager_started = true
    camp.sound_manager:update()
end

function start_harmonica(npc)
    local camp_id = db.storage[npc:id()].registred_camp
    if camp_id == nil then    
        return
        --abort("trying to play harmonica without camp")
    end
    local camp = bind_camp.camps[camp_id].camp

    camp.sound_manager:set_storyteller(camp.director)
    camp.sound_manager:set_story(camp.harmonica_table[math.random(#camp.harmonica_table)])
    camp.sound_manager_started = true
    camp.sound_manager:update()
end

function start_legend(npc)
    local camp_id = db.storage[npc:id()].registred_camp
    if camp_id == nil then
        return    
    end
    local camp = bind_camp.camps[camp_id].camp

    camp.sound_manager:set_storyteller(camp.director)
    camp.sound_manager:set_story(camp.legend_table[math.random(#camp.legend_table)])
    camp.sound_manager_started = true
    camp.sound_manager:update()
end

 

 

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


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

Бессмертный возможно ты забыл вызываемый звук прописать, в sr_camp

Попробуй _g.script раскоментировать error_log_reason, возможно более подробный даст лог.


Дополнено 3 минуты спустя

У кого есть модель вот такого нпс?

IMG_20250708_100549.jpg

  • Жму руку 1

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


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

RomaL23 обычный нпс с шейдером фантом.

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


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

AfterGlow это как. Ну точнее, как называется сама модель этого нпс?

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


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

RomaL23 сталкер нейтрал новис шото там.

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


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

RomaL23 Из мода вытащи

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


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

столкнулся с интересной проблемой, может кто подскажет
вот у меня есть два файла по пути editors\rawdata\levels 
эти два файла: spawn_old.part и spawn.part 
я долго ломал голову, почему игра пытается найти файлы которые я не инклудил, и смарты которые я удалил с локации.
Оказалось, что в spawn_old.part есть секции, которые как раз таки просит игра с таким вылетом:
Description   : There is no configuration file [scripts\escape\smart\esc_smart_terrain_3_0.ltx] in smart_terrain [esc_smart_terrain_3_0]

эти секции есть в spawn_old.part, вот секция из лога:
[object_844]
        attached_count                   = 1
        clsid                            = 6
        co_flags                         = 2
        name                             = esc_smart_terrain_3_0
        position                         = -23.051250, -12.322126, -119.508192
        rotation                         = 0.000000, 0.000000, 0.000000
        scale                            = 1.000000, 1.000000, 1.000000
        type                             = 2
        version                          = 23

в spawn.part этого всего нет.
Вопрос такой, а можно ли редактировать этот spawn_old.part?? убрать оттуда секции которые просит игра, например смарт террейны, файлы с логикой и так далее? если можно то что для этого надо? пересобирать локацию?

локация взята из мода, и вот эти секции остались с него именно в этом файле.

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


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

Как прописать путь залезания по лестнице для нпс? Может вопрос немного глупый, но что то не пойму) 

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

Волк волку- волк, человек человеку- собутыльник.

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


Ссылка на сообщение
Поделиться на другие сайты
 
55 минут назад, RomaL23 сказал:

у меня есть два файла по пути editors\rawdata\levels 

Видимо все же по пути editors\rawdata\levels\имя_уровня

55 минут назад, RomaL23 сказал:

два файла: spawn_old.part и spawn.part

Файл spawn.part SDK читает, файл spawn_old.part - нет.

55 минут назад, RomaL23 сказал:

почему игра пытается найти файлы которые я не инклудил, и смарты которые я удалил с локации.

Игра не знает о манипуляциях в SDK если вы должным образом не пере собрали all.spawn и не положили его куда следует.

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

Путь во мгле. Связь времен.
"Он ловко выхватил из-под себя табуретку и очень метко и сильно бросил ее в докладчика..." (Чугунный всадник)

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


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

denis2000 как это так. у меня all.spawn лежит по пути gamedata\spawns\all.spawn 
я ума не приложу почему игра просит секции из spawn_old.part. 

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


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

Ayden В оригинале НПС не лазят по вертикальным лестницам, из за того, что движком не предусмотрено проигрывание специальных анимаций (хотя они есть в библиотеке).


RomaL23 Игра просит секции из all.spawn, только и всего. Есть ли эти секции в каком либо другом файле этой вселенной ей по барабану.

Изменено пользователем denis2000
  • Лайк 1

Путь во мгле. Связь времен.
"Он ловко выхватил из-под себя табуретку и очень метко и сильно бросил ее в докладчика..." (Чугунный всадник)

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


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

denis2000 p.s.
вы мне скидывали локации из путь во мгле, я ещё несколько месяцев назад просил их. вот оттуда я и взял кордон, увы проблемный. rawdata прямо из того архива


Дополнено 5 минуты спустя

p.s. x2 
в spawn.part никаких этих секций нет, которые требует игра.

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


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

denis2000  Ну в том и дело, что он просто стоя без анимации поднимается) Получается надо делать отдельный участок с анимацией? 


Волк волку- волк, человек человеку- собутыльник.

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


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

Ayden Это логичное следствие, только, как без движка, синхронизировать перемещение и эти анимации я не представляю. Возможно получиться сделать отдельную схему поведения НПС для такого действия.

Короче, в качестве старта, попробуйте найти эти анимации и зацепить из при помощи схемы remark.


46 минут назад, RomaL23 сказал:

rawdata прямо из того архива

Эти наработки вы используете на свой страх и риск.

46 минут назад, RomaL23 сказал:

в spawn.part никаких этих секций нет, которые требует игра.

Ок. Еще раз: Игра не знает о манипуляциях в SDK если вы должным образом не пере собрали all.spawn и не положили его куда следует. Эти секции из all.spawn. 

Изменено пользователем denis2000
  • Лайк 1

Путь во мгле. Связь времен.
"Он ловко выхватил из-под себя табуретку и очень метко и сильно бросил ее в докладчика..." (Чугунный всадник)

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


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

denis2000 Спасибо! В принципе там немного, для одной сценки надо, вот думаю, будет ли это выглядеть нормально, там лестница импровизированная

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

Волк волку- волк, человек человеку- собутыльник.

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


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

Как можно сократить паузы между действиями (анекдот, игра на гитаре\гармошке) сталкеров у костра? Хочется сократить "неловкие" паузы и повысить частоту совершаемых ими действий.

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


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

А как сделать чтобы диалог нельзя было скипнуть?

Я в плане того что некоторые диалоги в игре можно по множеству раз "читать" как пример в ЧН когда разговариваешь с Лесником и если нажать клавишу ESC то можно кучу раз активировать диалог и следовательно он будет выдавать ГГ кучу винторезов в награду за основной квест. (p.s я сейчас не про то как исправить данный баг с винторезом)

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

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


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

Paradox27kms в логику нпс, в секцию meet:

allow_break = false

  • Мастер! 1

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


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

При добавлении новых строчек в титрах игры ловлю вылет

Спойлер

Expression    : false
Function      : CXml::Load
File          : E:\priquel\sources\engine\xrXMLParser\xrXMLParser.cpp
Line          : 87
Description   : XML file:ui\game_tutorials.xml value: errDescr:Error reading Attributes.

Редактировал ui_credits_base.xml

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


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

Только начинаю делать моды на ЗП. Совсем не могу понять, как декомпилировать локации. SDK скачал, а локаций нет.

P.S. Если если возможность, я буду рад проконсультироваться с кем-то лично, кто шарит в моддинге на ЗП и не откажется помочь.

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


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

Paradox27kms Косяки с атрибутами в тегах текста?


Flicker652 Задавайте конкретный вопрос в соответствующей теме.

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

Путь во мгле. Связь времен.
"Он ловко выхватил из-под себя табуретку и очень метко и сильно бросил ее в докладчика..." (Чугунный всадник)

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


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

denis2000 спс разобрался самостоятельно.

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


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

Возможно ли сделать диалоговое окно для двух-трёх и т.д. персонажей? Если да, как это можно сделать? Я помню точно, такое было реализовано в одном из модов на ТЧ.

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


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

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

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

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

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

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

Войти

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

Войти

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

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