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

Пользовательская функция горячих клавиш формы udf1


Pitronic

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

Вопрос меня давно волновал, но забывал спросить. Когда то скачал, не помню для какой игры трейнер. Там была такая пользовательская фишка. Там где описания горячих клавиш. Была кнопка edit при активации которой пользователь мог изменить горячие клавиши которые там по умолчанию. Можно ли реализовать это в ce на форме udf1. Если кто знает подскажите пожалуйста пж по шагам.

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

4 минуты назад, LIRW сказал:

Можно ли реализовать это в ce на форме udf1

Я там не чего не понял. и втой теме по ходу делу форма не udf1 а 

CETrainer

Изменено пользователем Pitronic
Ссылка на комментарий
Поделиться на другие сайты

Спойлер

image.png

 

 

Разобраться будет непросто. Основной принцип надо уловить.

1) userDefinedKeys - Lua-таблица с hot-keys. Не более 5 комбинаций нажатий. В эту таблицу происходит запись и чтение.

2) (object1.ClassName=='tcheat') and (object2.ClassName=='TMemoryRecordHotkey') это типы недокументированные в celua.txt. Экземпляры этих типов генерируются на форме при создании hot-keys

Спойлер

image.png

3) Сколько опций нагенерировали на форме, столько надо добавить потом. например

addChangeHotkeyKeysFunctionality(CETrainer.CHEAT0,memrec0_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT1,memrec1_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT2,memrec2_hotkey0)

4) userPressedKey - обработчик нажатий в поле ввода текста, который будет работать с таблицей кодов hotkeys userDefinedKeys на 5 нажатий

5) clearHotkey() - обработчик клика на кнопку, очистит таблицу кодов hotkeys userDefinedKeys

6) -- создание формы changeHotkeyKeysForm "local function formCreate()". 

 

Принцип, который надо понять и уловить состоит в подмене хоткеев в tcheat и TMemoryRecordHotkey.  Как именно подменяется можно увидеть по исходникам и по документации. К сожалению, документировано не все.  Те, кто постоянно лазят на форуме CE, по исходником CE они знают и описание tcheat  и TMemoryRecordHotkey.

 

С моими комментариями:

 

Спойлер
-- обработчик нажатий до 5 hot-keys на поле для поля CEEdit1.OnKeyDown = userPressedKey (ниже задается)
-- userDefinedKeys = {} таблица hot-keys
local function userPressedKey(sender, key)
  if userDefinedKeys[5]==0 then
    for i=1,5 do
      if userDefinedKeys[i]==0 then
        userDefinedKeys[i]=key
        break
      else
        if userDefinedKeys[i]==key then break end
      end
    end
  end
  -- преобразование таблицы hot-keys в строку и запись в поле changeHotkeyKeysForm.CEEdit1.Text
  changeHotkeyKeysForm.CEEdit1.Text=convertKeyComboToString(userDefinedKeys)
  return 0
end
-- Обработчик для очистки hot-key
local function clearHotkey()
  userDefinedKeys={0,0,0,0,0}
  changeHotkeyKeysForm.CEEdit1.Text=convertKeyComboToString(userDefinedKeys)
  changeHotkeyKeysForm.CEEdit1.setFocus()
end
-- создание формы changeHotkeyKeysForm
local function formCreate()
  changeHotkeyKeysForm=createForm(false)
  changeHotkeyKeysForm.Name = 'changeHotkeyKeysForm'
  changeHotkeyKeysForm.Caption = 'Hotkey Manually Set'
  changeHotkeyKeysForm.Width = 300
  changeHotkeyKeysForm.Height = 120
  changeHotkeyKeysForm.Position = poScreenCenter
  changeHotkeyKeysForm.OnClose =
     function ()
       changeHotkeyKeysForm.CEEdit1.setFocus()
       return caHide
     end

  local CELabel1=createLabel(changeHotkeyKeysForm)
  CELabel1.Name = 'CELabel1'
  CELabel1.Left = 20
  CELabel1.Top = 20
  CELabel1.Caption = 'Set hotkey:'

  local CEEdit1=createEdit(changeHotkeyKeysForm)
  CEEdit1.Name = 'CEEdit1'
  CEEdit1.Text = ''
  CEEdit1.AnchorSideLeft.Control = CELabel1
  CEEdit1.AnchorSideTop.Control = CELabel1
  CEEdit1.AnchorSideTop.Side = asrBottom
  CEEdit1.Height = 25
  CEEdit1.Width = 248
  CEEdit1.BorderSpacing.Top = 4

  local clearButton=createButton(changeHotkeyKeysForm)
  clearButton.Name = 'clearButton'
  clearButton.AnchorSideLeft.Control = CEEdit1
  clearButton.AnchorSideTop.Control = CEEdit1
  clearButton.AnchorSideTop.Side = asrBottom
  clearButton.Height = 30
  clearButton.Width = 80
  clearButton.BorderSpacing.Top = 8
  clearButton.Caption = 'Clear'

  local applyButton=createButton(changeHotkeyKeysForm)
  applyButton.Name = 'applyButton'
  applyButton.AnchorSideLeft.Control = clearButton
  applyButton.AnchorSideLeft.Side = asrBottom
  applyButton.AnchorSideTop.Control = clearButton
  applyButton.Height = 30
  applyButton.Width = 80
  applyButton.BorderSpacing.Left = 10
  applyButton.Caption = 'Apply'

  CEEdit1.OnKeyDown = userPressedKey -- обработчки нажатий
  -- https://lazarus-ccr.sourceforge.io/docs/lcl/controls/tmousebutton.html для записи хот-кей мышки.
  local mbtn={false,true,true,true}
  CEEdit1.OnMouseDown = function (s,k) if mbtn[k] then s.OnKeyDown(s,k+2) end end
  
  clearButton.OnClick = clearHotkey    -- обработчик очистки hot-keys
  applyButton.ModalResult = mrYes      -- apply button
end

-- Обработчик кнопки смены hot-key. Показывает форму смены hot-keys
function changeHotkeyKeys(hotkey)
  -- если форма не создана, то создать
  if not changeHotkeyKeysFormCreated then
    changeHotkeyKeysFormCreated = true
    formCreate()
  end

  if hotkey==nil then return end
  -- таблица из пользовательский hot-keys. Максимум 5
  userDefinedKeys={0,0,0,0,0}
  -- проверка на свопаданеие для ClassName=='TGenericHotkey' или на "TMemoryRecordHotkey"
  if hotkey.ClassName=='TGenericHotkey' then
    -- если TGenericHotkey, то запись клавиш
    -- TGenericHotkey, вроде, не документирована. Так что либо лезем в git либо запоминаем что есть .getKeys() у hotkey
    for i,v in ipairs({hotkey.getKeys()}) do
      userDefinedKeys[i]=v -- установка hot-key
    end
    -- показать таблицу с кодами hot-keys как строку
    changeHotkeyKeysForm.CEEdit1.Text=convertKeyComboToString(userDefinedKeys)
    -- запомнить что есть setKeys у  hotkey типа TGenericHotkey. Передать пять нажатий
    if changeHotkeyKeysForm.showModal()==mrYes then
      hotkey.setKeys(userDefinedKeys[1],userDefinedKeys[2],
                     userDefinedKeys[3],userDefinedKeys[4],
                     userDefinedKeys[5])
    end

  elseif hotkey.ClassName=='TMemoryRecordHotkey' then -- если же это TMemoryRecordHotkey
    for i,v in ipairs(hotkey.Keys) do
      userDefinedKeys[i]=v -- записать коды hot-key
    end
    -- показать строку
    changeHotkeyKeysForm.CEEdit1.Text=convertKeyComboToString(userDefinedKeys)
    -- если подвержедение закрытия формы через showModal()==mrYes, то назначить клавиши
    if changeHotkeyKeysForm.showModal()==mrYes then
      hotkey.Keys = userDefinedKeys
    end

  end

end

-- Функция для связи CETrainer.CHEAT0 и memrec0_hotkey0. Примеры ниже.
-- object1 это тип условный тип tcheat, object2 это тип TMemoryRecordHotkey. Типы не документированы в celua.txt. Просто запомнить или лезть в git CE
function addChangeHotkeyKeysFunctionality(object1, object2)
  if (type(object1)=='userdata') and (object1.ClassName=='tcheat') and (object2.ClassName=='TMemoryRecordHotkey') then
    local btn = createButton(object1.Owner)
    btn.Parent = object1.Parent
    btn.AnchorSideLeft.Control = object1
    btn.AnchorSideTop.Control = object1
    btn.Height = object1.Height
    btn.Width = 15
    btn.BorderSpacing.Left = object1.Descriptionleft - 20
    btn.Caption = 'E' -- создание кнопок вида "E" редактироавния hot-key
    btn.OnClick = function () changeHotkeyKeys(object2) object1.Hotkey = object2.HotkeyString end
  end
end

-- автоматическая, генерация как обычно. Оставить как есть для трех опций.

--TRAINERGENERATORSTART--
--This is autogenerated code. Changing code in this block will
--get erased and rewritten if you regenerate the trainer code

--Uncomment the following line if this is a Cheat Table format trainer and you don't want CE to show (Tip, save as .CETRAINER alternatively)
--hideAllCEWindows()

RequiredCEVersion=6.6
if (getCEVersion==nil) or (getCEVersion()<RequiredCEVersion) then
  messageDialog('Please install Cheat Engine '..RequiredCEVersion, mtError, mbOK)
  closeCE()
end
addresslist=getAddressList()
memrec0=addresslist.getMemoryRecordByID(0)
memrec1=addresslist.getMemoryRecordByID(1)
memrec2=addresslist.getMemoryRecordByID(2)

memrec0_hotkey0=memrec0.getHotkeyByID(0)
memrec1_hotkey0=memrec1.getHotkeyByID(0)
memrec2_hotkey0=memrec2.getHotkeyByID(0)

function onPostHotkey0(Hotkey)
  --Executed after the "toggle*" cheat got executed
  local memrec=Hotkey.Owner
  local isActive=memrec.Active --get the state after the hotkey got triggered
  CETrainer.CHEAT0.setActive(isActive) --gui update, nothing else
  if gPlaySoundOnAction then
    if isActive then
      playSound(gActivateSound)
    else
      playSound(gDeactivateSound)
    end
  end
end

memrec0_hotkey0.onPostHotkey=onPostHotkey0

function onPostHotkey1(Hotkey)
  --Executed after the "toggle*" cheat got executed
  local memrec=Hotkey.Owner
  local isActive=memrec.Active --get the state after the hotkey got triggered
  CETrainer.CHEAT1.setActive(isActive) --gui update, nothing else
  if gPlaySoundOnAction then
    if isActive then
      playSound(gActivateSound)
    else
      playSound(gDeactivateSound)
    end
  end
end

memrec1_hotkey0.onPostHotkey=onPostHotkey1

function onHotkey2(Hotkey)
  --Executed before the hotkey is handled
  CETrainer.CHEAT2.setActive(true, 1500)
  if gPlaySoundOnAction then
    playSound(gActivateSound)
  end
end

memrec2_hotkey0.onHotkey=onHotkey2
CETrainer.SEPERATOR.Visible=false

getAutoAttachList().add("ddgd")
gPlaySoundOnAction=false
CETrainer.show()
function AboutClick()
  showMessage(gAboutText)
end
gAboutText=[[This trainer was made by Cheat Engine
www.cheatengine.org]]

function CloseClick()
  --called by the close button onClick event, and when closing the form
  closeCE()
  return caFree --onClick doesn't care, but onClose would like a result
end

--TRAINERGENERATORSTOP--

-- А это уже дописывать. Сколько опций, столько и строк
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT0,memrec0_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT1,memrec1_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT2,memrec2_hotkey0)

 

 

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

Если что не понятно, то пиши. Я когда исходник открыл вообще ничего не понял. Когда начинаешь разбираться, потом картина выстраивается.

 

Если бы я делал с нуля, то, наверно, делал бы без tcheat и TMemoryRecordHotkey, но остальное почти также с convertKeyComboToString

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

22 часа назад, MasterGH сказал:

Если бы я делал с нуля, то

А как бы сделали вы? Хочу попробовать оба варианта этот и ваш и сравнить какой удобней. Если можно то тоже с коментами как здесь. Но не настаиваю просто если есть время и возможность.

 

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

Я не особо любитель автономных трейнеров. Если бы я заморочился, то т.к. форма генерируемого трейнера продолжает работать с обычными MemoryRecord  из таблицы CE, то можно в MemoryRecord подменять Hotkey. Я бы все удалил с формы. Создал бы сначала CECheckbox1, CEEdit1 и связал бы их с некоторым MemoryRecord по уникальному названию. В MemoryRecord подменял бы Hotkey через обработчик CEEdit1. По esc клавише стирать все, что в CEEdit1

Спойлер

image.png

Потом бы для остальных записей по аналогии...

На мой взгляд, это большие заморочки и сложнее чем если писать на WinAPI + язык программирования.  Я бы предложил просто пользоваться  *.CT-таблицей 🙂 

 

  • Плюс 1
Ссылка на комментарий
Поделиться на другие сайты

В 22.11.2022 в 20:47, MasterGH сказал:

Вариант, рабочий.

Не много не рентабельный скрипт. Необходимо два раза генерировать, чтобы потом в таблицу не достающие строки добавить. Методом тыка удаляя функции и проверяя работоспособность, переделал. Теперь после создании формы можно просто сохранить в exe, и нет необходимости второй раз генерировать из за того что первый раз генерировал чтоб в таблицу строки добавить. Вот ваш доработанный мною код.

Спойлер
-- обработчик нажатий до 5 hot-keys на поле для поля CEEdit1.OnKeyDown = userPressedKey (ниже задается)
-- userDefinedKeys = {} таблица hot-keys
local function userPressedKey(sender, key)
  if userDefinedKeys[5]==0 then
    for i=1,5 do
      if userDefinedKeys[i]==0 then
        userDefinedKeys[i]=key
        break
      else
        if userDefinedKeys[i]==key then break end
      end
    end
  end
  -- преобразование таблицы hot-keys в строку и запись в поле changeHotkeyKeysForm.CEEdit1.Text
  changeHotkeyKeysForm.CEEdit1.Text=convertKeyComboToString(userDefinedKeys)
  return 0
end
-- Обработчик для очистки hot-key
local function clearHotkey()
  userDefinedKeys={0,0,0,0,0}
  changeHotkeyKeysForm.CEEdit1.Text=convertKeyComboToString(userDefinedKeys)
  changeHotkeyKeysForm.CEEdit1.setFocus()
end
-- создание формы changeHotkeyKeysForm
local function formCreate()
  changeHotkeyKeysForm=createForm(false)
  changeHotkeyKeysForm.Name = 'changeHotkeyKeysForm'
  changeHotkeyKeysForm.Caption = 'Редактор горячих клавишь'
  changeHotkeyKeysForm.Width = 300
  changeHotkeyKeysForm.Height = 120
  changeHotkeyKeysForm.Position = poScreenCenter
  changeHotkeyKeysForm.OnClose =
     function ()
       changeHotkeyKeysForm.CEEdit1.setFocus()
       return caHide
     end

  local CELabel1=createLabel(changeHotkeyKeysForm)
  CELabel1.Name = 'CELabel1'
  CELabel1.Left = 20
  CELabel1.Top = 20
  CELabel1.Caption = 'Горячая клавиша:'

  local CEEdit1=createEdit(changeHotkeyKeysForm)
  CEEdit1.Name = 'CEEdit1'
  CEEdit1.Text = ''
  CEEdit1.AnchorSideLeft.Control = CELabel1
  CEEdit1.AnchorSideTop.Control = CELabel1
  CEEdit1.AnchorSideTop.Side = asrBottom
  CEEdit1.Height = 25
  CEEdit1.Width = 248
  CEEdit1.BorderSpacing.Top = 4

  local clearButton=createButton(changeHotkeyKeysForm)
  clearButton.Name = 'clearButton'
  clearButton.AnchorSideLeft.Control = CEEdit1
  clearButton.AnchorSideTop.Control = CEEdit1
  clearButton.AnchorSideTop.Side = asrBottom
  clearButton.Height = 30
  clearButton.Width = 80
  clearButton.BorderSpacing.Top = 8
  clearButton.Caption = 'Очистить'

  local applyButton=createButton(changeHotkeyKeysForm)
  applyButton.Name = 'applyButton'
  applyButton.AnchorSideLeft.Control = clearButton
  applyButton.AnchorSideLeft.Side = asrBottom
  applyButton.AnchorSideTop.Control = clearButton
  applyButton.Height = 30
  applyButton.Width = 80
  applyButton.BorderSpacing.Left = 10
  applyButton.Caption = 'Применить'

  CEEdit1.OnKeyDown = userPressedKey -- обработчки нажатий
  -- https://lazarus-ccr.sourceforge.io/docs/lcl/controls/tmousebutton.html для записи хот-кей мышки.
  local mbtn={false,true,true,true}
  CEEdit1.OnMouseDown = function (s,k) if mbtn[k] then s.OnKeyDown(s,k+2) end end

  clearButton.OnClick = clearHotkey    -- обработчик очистки hot-keys
  applyButton.ModalResult = mrYes      -- apply button
end

-- Обработчик кнопки смены hot-key. Показывает форму смены hot-keys
function changeHotkeyKeys(hotkey)
  -- если форма не создана, то создать
  if not changeHotkeyKeysFormCreated then
    changeHotkeyKeysFormCreated = true
    formCreate()
  end

  if hotkey==nil then return end
  -- таблица из пользовательский hot-keys. Максимум 5
  userDefinedKeys={0,0,0,0,0}
  -- проверка на свопаданеие для ClassName=='TGenericHotkey' или на "TMemoryRecordHotkey"
  if hotkey.ClassName=='TGenericHotkey' then
    -- если TGenericHotkey, то запись клавиш
    -- TGenericHotkey, вроде, не документирована. Так что либо лезем в git либо запоминаем что есть .getKeys() у hotkey
    for i,v in ipairs({hotkey.getKeys()}) do
      userDefinedKeys[i]=v -- установка hot-key
    end
    -- показать таблицу с кодами hot-keys как строку
    changeHotkeyKeysForm.CEEdit1.Text=convertKeyComboToString(userDefinedKeys)
    -- запомнить что есть setKeys у  hotkey типа TGenericHotkey. Передать пять нажатий
    if changeHotkeyKeysForm.showModal()==mrYes then
      hotkey.setKeys(userDefinedKeys[1],userDefinedKeys[2],
                     userDefinedKeys[3],userDefinedKeys[4],
                     userDefinedKeys[5])
    end

  elseif hotkey.ClassName=='TMemoryRecordHotkey' then -- если же это TMemoryRecordHotkey
    for i,v in ipairs(hotkey.Keys) do
      userDefinedKeys[i]=v -- записать коды hot-key
    end
    -- показать строку
    changeHotkeyKeysForm.CEEdit1.Text=convertKeyComboToString(userDefinedKeys)
    -- если подвержедение закрытия формы через showModal()==mrYes, то назначить клавиши
    if changeHotkeyKeysForm.showModal()==mrYes then
      hotkey.Keys = userDefinedKeys
    end

  end

end

-- Функция для связи CETrainer.CHEAT0 и memrec0_hotkey0. Примеры ниже.
-- object1 это тип условный тип tcheat, object2 это тип TMemoryRecordHotkey. Типы не документированы в celua.txt. Просто запомнить или лезть в git CE
function addChangeHotkeyKeysFunctionality(object1, object2)
  if (type(object1)=='userdata') and (object1.ClassName=='tcheat') and (object2.ClassName=='TMemoryRecordHotkey') then
    local btn = createButton(object1.Owner)
    btn.Parent = object1.Parent
    btn.AnchorSideLeft.Control = object1
    btn.AnchorSideTop.Control = object1
    btn.Height = object1.Height
    btn.Width = 15
    btn.BorderSpacing.Left = object1.Descriptionleft - 15
    btn.Caption = 'E' -- создание кнопок вида "E" редактироавния hot-key
    btn.OnClick = function () changeHotkeyKeys(object2) object1.Hotkey = object2.HotkeyString end
  end
end

addresslist=getAddressList()
memrec0=addresslist.getMemoryRecordByID(0)
memrec1=addresslist.getMemoryRecordByID(1)
memrec2=addresslist.getMemoryRecordByID(2)
memrec3=addresslist.getMemoryRecordByID(3)
memrec4=addresslist.getMemoryRecordByID(4)
memrec5=addresslist.getMemoryRecordByID(5)
memrec6=addresslist.getMemoryRecordByID(6)
memrec7=addresslist.getMemoryRecordByID(7)
memrec8=addresslist.getMemoryRecordByID(8)
memrec9=addresslist.getMemoryRecordByID(9)

memrec0_hotkey0=memrec0.getHotkeyByID(0)
memrec1_hotkey0=memrec1.getHotkeyByID(0)
memrec2_hotkey0=memrec2.getHotkeyByID(0)
memrec3_hotkey0=memrec3.getHotkeyByID(0)
memrec4_hotkey0=memrec4.getHotkeyByID(0)
memrec5_hotkey0=memrec5.getHotkeyByID(0)
memrec6_hotkey0=memrec6.getHotkeyByID(0)
memrec7_hotkey0=memrec7.getHotkeyByID(0)
memrec8_hotkey0=memrec8.getHotkeyByID(0)
memrec9_hotkey0=memrec9.getHotkeyByID(0)

addChangeHotkeyKeysFunctionality(CETrainer.CHEAT0,memrec0_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT1,memrec1_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT2,memrec2_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT3,memrec3_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT4,memrec4_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT5,memrec5_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT6,memrec6_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT7,memrec7_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT8,memrec8_hotkey0)
addChangeHotkeyKeysFunctionality(CETrainer.CHEAT9,memrec9_hotkey0)

gPlaySoundOnAction=false
CETrainer.fixDPI()
CETrainer.show()
function CloseClick()
  closeCE()
  return caFree
end

 

А вот образец таблицы Тык Зацените по десяти бальной шкале.

Изменено пользователем Pitronic
исправление грамм ошибок
Ссылка на комментарий
Поделиться на другие сайты

Скорее всего,  так решил Dark Byte и посчитал, что такое число нажатий достаточно.  Число нажатий можно проверить в окне установки hotkeys при редактировании memory record через контекстное меню

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

47 минут назад, MasterGH сказал:

так решил Dark Byte

Понял. А как моя редакция скрипта? Я просил оценить в этой теме не кто не откликнулся.

Изменено пользователем Pitronic
Дополнение.
Ссылка на комментарий
Поделиться на другие сайты

12 минут назад, Pitronic сказал:

Понял. А как моя редакция скрипта?

Наверно, надо целиком таблицу .CT смотреть. Так ошибки.

По скрипту и сравнению с прошлой версией, вроде, нормально. 

 

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

43 минуты назад, MasterGH сказал:

Наверно, надо целиком таблицу .CT

Так я же скинул там таблицу. Всё работает. На десять читов сделал. вот ссылка на топик где я скинул ссылку здесь  под показать контент увидите ( А вот образец таблицы Тык Зацените по десяти бальной шкале. )

Изменено пользователем Pitronic
Ссылка на комментарий
Поделиться на другие сайты

Да забыл сказать, я там в форме что то не так сделал. форма трейнера сзади таблицы получилась. Просто после запроса ( выполнить скрипт ) подтвердить и подвинуть в сторону таблицу. Можно тестировать. Увидите. Всё работает.

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

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.