Перейти к содержимому

vladikcomper

Пользователи
  • Публикации

    947
  • Зарегистрирован

  • Посещение

  • Дней в лидерах

    8

Сообщения, опубликованные пользователем vladikcomper


  1. Дополнительный слой заднего плана -- лишь небольшой графический трюк; слой имитируется отрисовкой в нужных местах тайлов, которые анимируется вместе с движением камеры, создавая эффект "окна" в различных местах заднего плана, через которое можно увидеть якобы более удаленные его участки.

    Анимация тайлов сделана так, чтобы создавать эффект скроллинга вместе с движением камеры -- графика внутри тайлов "прокручивается", тайлы переходят друг в друга, создавая эффект цельного, непрерывного плана.

    Этот эффект довольно несложно запрограммировать, есть даже несправедливо забытый гид на эту тему:

    https://forums.sonicretro.org/index.php?showtopic=24664 (к сожалению, картинки в нем не пощадило время)


  2. Огромное спасибо всем за поздравления :3

     

    Влад, а твой подарок просто шикарен. Великолепная работа! Такого необычного подарка я и представить не мог! Спасибо тебе, S_T_D и Ivan_YO.

     

    С днём рождения! Удачи в Hacking Contest, если участвуешь в этом году. :)

    Да, я учавствую, и немного удачи мне не помешает ;)

    • Лайк 3

  3. Снова.Снова...Снова у меня проблема

     

    Решил вставить лого Соник тим(Зачем?Да я сам не знаю!) вместо овала в титл кардс.Вставил,мапинги настроил.И тут батс!

    h_1440408217_6625912_0e55428632.png

     

    <...>

     

    Все дело в тайлах. Ты значительно увеличил размер арт-файла после добавления большого количества новых тайлов. Когда этот арт будет загружен в видеопамять, он, естественно, займет значительно больше места, чем задумано, и начнет занимать те участки памяти, где он быть не обязан, а именно, маппинги плана А.

     

    Поэтому в начале уровня портится сам логотип: его тайлы, вышедшие за дозволенные им рамки, перепишет план А, а в конце уровня -- сам План А, т.к. его в этот раз перепишут тайлы логотипа, поскольку они в этом случае загружаются позднее.

     

    За подробностями почитай этот пост. У тебя такая же проблема.

    http://sonic-world.ru/forum/topic/16333-%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D1%8C-%D0%BF%D0%BE-%D1%80%D0%BE%D0%BC%D1%85%D0%B0%D0%BA%D0%B8%D0%BD%D0%B3%D1%83/page-49?do=findComment&comment=252896132


  4. Одна из самых распространенных проблем с самым простым решением. За все время она поднималась огромное количество раз. Только в этой теме как минимум десяток.

     

    http://sonic-world.ru/forum/topic/16333-помощь-по-ромхакингу/?p=252358280


  5. Как редактировать маппинги вручную

     

     

    В Соник 1 кадр спрайтовых маппингов (sprite mappings frame) имеет следующий формат:

     

    Первый байт указывает количество кусочков, из которых состоит целый спрайт (pieces в терминологии SonMapEd), считая с $01.

    Значение $00 указывает, что кадр пустой и не содержит в себе никаких кусочков.

     

    Далее расположены данные самих кусочков -- структуры из 5 байт, имеющие следующий формат:

     

        dc.b    $YY, %WWHH, $TT, $TT, $XX

     

    Для удобства я представил уже готовую запись с указанием форматов полей.

    Символ $ указывает, что значение имеет шестнадцатеричное представление (hex, hexadecimal).

    Символ % указывает, что значение имеет двоичное представление (bin, binary). В таком преставлении удобнее задавать битовые поля, которые используются в некоторых байтах структуры. В действительности, не имеет значения, в каком представлении записаны данные, программист сам выбирает наиболее удобное ему представление для конкретного случая.

     

    Поля $XX и $YY представляют собой смещение кусочка относительно центра спрайта (центральной точки) в пикселях.

    Все спрайты в игре обычно центрируются, т.е. X и Y координаты объекта обычно привязаны к центру спрайта. Получается это благодаря тому, что кусочкам задают такие смещения, чтобы они располагались строго вокруг центральной точки (точки отсчета, координат объекта).

     

    Поле %WWHH кодирует ширину и высоту кусочка согласно внутреннему формату спрайтов VDP.

    Биты WW и HH могут принимать следующие значения:

    00 -- 8 пикселей

    01 -- 16 пикселей

    10 -- 24 пикселя

    11 -- 32 пикселя

    Например, чтобы если необходимо задать кусочек размером 16х24 пикселя, то WW=01, HH=10. Получаем: %0110

     

    Поле $TT TT как видно, занимает 2 байта. В этом поле указывается индекс стартового тайла для кусочка относительно арт-поинтера (слова $02 структуры объекта, которое задает начало тайлов объекта в видеопамяти).

     

    Замечу, что в этом поле еще есть дополнительные биты, которые часто не используются и остаются нулевыми. Чтобы рассмотреть формат этого поля более подробно, представлю его побитовую запись:

    %PCCXYTTT TTTTTTTT

     

    Бит X -- флаг отражения по оси X (0 -- нет, 1 -- отражение по горизонтали).

    Бит Y -- флаг отражения по оси Y (0 -- нет, 1 -- отражение по вертикали).

    Биты CC -- строка палитры, которую использует спрайт (00 -- первая, 01 -- вторая, 10 -- третья, 11 -- четвертая). Строку палитры обычно задают через арт-поинтер.

    Бит P -- флаг приоритета. Если задан, спрайт будет отображен поверх всех (подобно HUD).

     

    Пример прямоугольника 24х40:

        dc.b    $02
        dc.b    $EC, %1011, $00, $00, $F4
        dc.b    $0C, %1000, $00, $0C, $F4

     

    Как сделать так,что бы когда Соник джампдешил,за ним следовали его пред-идущие положения?

     

    Простой и весьма очевидный ответ -- нужно отображать дополнительные спрайты, которые копируют спрайты Соника в тех местах, где объект Соника находился мгновения назад.

     

    Движок игры спроектирован так, что определенный спрайт отображается определенным объектом. Понятия спрайт и объект неразрывно связаны в Соник 1 и сиквелах, включая даже Соник 3, в котором движок был значительно переделан. Поэтому в дизасембле Sonic & Knuckles объекты во многих местах названы спрайтами.

     

    Можно сказать, объект представляет собой спрайт, его координаты, состояние, анимацию и так далее. Из этого следует продолжение ответа на твой вопрос -- чтобы отображать дополнительные спрайты, нужно загружать дополнительные объекты -- ровно столько, сколько спрайтов еще нужно отобразить.

     

    Самым разумным решением будет создать новый объект для отображения предыдущих положений Соника.

     

    Узнать сами положения Соника не представляется большой проблемой -- игра все время ведет специальный массив предыдущих положений Соника, который используется объектом Invincibility stars.

     

    Массив хранится в области памяти $CB00-$CBFF.

    Переменная типа word по адресу памяти $F7A8 указывает на текущий элемент массива. Для загрузки предыдущих положений достаточно узнать текущий элемент массива и загрузить выборочно элементы, предст шествующие текущему.

    За примерами реализаций этих задач можно обратиться к тому самому объекту Invincibility stars.

    • Лайк 3

  6. Помню мой старый мини-гид прямиком из одного поста в теме "Ваши хаки" пятилетней давности.

    Реализация максимально проста, оттого и чувствительна к разного рода ошибкам. Палитра третьего акта может сбиться после неудачного обновления палитры второго акта, потому что ее местоположение напрямую зависит от прошлых палитр.

     

    После следования гиду для GHZ у тебя должно получиться:

    Pal_GHZ:		incbin  pallet\ghz1.bin
    			incbin  pallet\ghz2.bin
    			incbin  pallet\ghz3.bin
    

    Важно, чтобы все файлы присутствовали и были корректного размера.

    Палитра уровня должна состоять из 48 цветов (3 строки палитры по 16 цветов, первая строка, содержащая палитру Соника, общая для всех уровней), т.е. размер каждого из файлов (ghz1.bin, ghz2.bin, ...) должен быть ровно 96 байт. В противном случае, данные окажутся смещены и палитра для нужного акта попросту не окажется на том месте, где этого ожидает игра.

     

    Если все файлы палитры были скопированы и включены правильно, остается только один вариант: содержимое какого-то из файлов было нарушено и он содержит больше или меньше строк палитры, чем должен.

     

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

     

    Действительно, если насильственно продублировать первую строку палитры, сместив остальные, результат будет тот же:

    Screenshot%202015-07-23%2013.12.31.png


  7. Проблема не в объекте "Press Start Button", он не может повлиять на координаты заднего и переднего плана (на переднем плане и отрисовывается основа титульного экрана). А вот что может, так это сам Соник, точнее его начальные координаты.

     

    Видишь ли, титульный экран по сути использует движок, на котором работают обычные уровни игры. Он частично загружает данные уровня GHZ1, что включает даже начальные позиции Соника -- именно поэтому если изменить начальные координаты Соника в GHZ1, титульник очень часто сбивается -- ведь там работает полноценная камера, которая работает и на обычных уровнях.

     

    Это пример очень плохого, ненадежного программирования, но такой подход был очень быстрым и количество кода сократилось до минимума -- в остальном используются те же общие процедуры, которые используются самими уровнями.

     

    Самый легкий способ исправить проблему - переместить Соника чуть выше на уровне GHZ1, тогда и камера при загрузке уровня (а титульник, как я уже и сказал, по сути частично загружает уровень GHZ1) будет расположена выше, так что титульный экран на переднем плане не сместится ниже положенного.

     

    Кто-то на SSRG или Sonic Retro предлагал довольно верное решение проблемы, но боюсь, я так и не смог найти этот пост: он потерялся со временем. В целом, достаточно лишь исправить позицию камеры, однако лично я не пробовал это, т.к. не было необходимости (в моих хаках титульные экраны я пишу с нуля), поэтому готового и рабочего решения предложить не могу.

    • Лайк 3

  8.  

     

    Боже, не стоит снова вспоминать и пытаться распространить здесь эту нечисть.

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

    Да и зачем нужен кривой и грязный Хомминг, когда в свободном доступе уже давно есть это: http://pastebin.com/A2n9n88Z

     

    Не знаю в чем дело, но при попытке открыть пресловутый сайт, моя система полностью провисла на несколько минут, а браузер висел еще минут 5, пока у меня не кончилось терпение, и я не решил его убить через диспетчер задач, но и тот на полминуты провис, пытаясь связаться с процессом. Но и это еще не все! Даже модем неожиданно для меня вырубило и он ушел в ребут по загадочным обстоятельствам. Никогда такого не было.

     

    В общем, система приходила в себя около 10 минут, которые длились вечность.

    Как одна простая ссылка на пресловутый сайт может вызвать столько невозможных проблем -- не укладывается в голове.

    Может дело действительно в нечисти?

    • Лайк 4

  9. Вот что произошло: я юзал СонЭд, и когда я сохранил изменённые парочку тайлов, мои файлы с графой и уровнями повредились. От файла с графикой, остались только первые примерно 15-20 тайлов. Файл с уровнями стерт. (вернее, он стал весть 0 байт, но я его особо не редактировал) Что делать с новой графикой, на создание которой уходили недели, если у файла точек восстановления нет? Утеряно?

     

    Это баг SonED2. Происходит, если закрыть окно программы слишком рано после нажатия клавиши F3 для сохранения.

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


  10. Загрузка босса GHZ происходит в Resize_GHZ3. Можешь просто скопировать весь код и поместить его на место Resize_SYZ1 (переименовав все лейблы, что бы названия не повторялись). То же самое и с боссом SLZ. Resize_SLZ3 на Resize_SYZ2. Если не понятно, вот готовый пример http://yadi.sk/d/TowTkqDsKCAmY. Тут только босс GHZ добавлен (графика кривая, думаю сможешь исправить), босса на второй акт думаю сможешь поставить сам по примеру первого акта.

     

    Совершенно верно.

     

    Но помимо этого нужно еще позаботиться, чтобы код SignpostArtLoad не выполнялся для уровня, на котором будет босс, в противном случае этот код будет конфликтовать с кодом, блокирующим камеру на арене босса и заблокировать камеру должным образом не получится.


  11. Это значит, что эту программу можно отключить, однако игра будет долго запускаться? Или как? 

     

    Программу можно отключить и не изменится ровным счетом ничего. Ну если только build.bat будет работать на 0.05 секунды быстрее.


  12.  

     

    хм... Давайте зададим вопрос создателю батника и комментатору кода красного экрана. vladikcomper, зачем надо было вставлять в батник программу которая фиксит чексумму, и при этом же комментировать код красного экрана?

    Это действительно хороший вопрос...

     

    Не я был создателем этого батника, он идет из оригинального дизасембла Hivebrain'а 2005 года. Есть в нем, правда, одно нововведение, заключающееся в генерации файла sonic1.lst после компиляции, в котором отображается оффсет и машинный код каждой скомпилированной инструкции (бесценно для отладки хаков с дебаггерами и без).

     

    Но действительно, вызов программы для исправления чек-суммы уже имеет мало смысла. Не помню точно, почему она осталась (дело было в 2010-м, четыре года назад как никак!). Скорее всего, просто забыл.

     

    Впрочем, это не означает, что алгоритм проверки чек-суммы из кода игры я убирал зря. Дело в том, что так игра попросту запускается быстрее, ведь перебор всех слов в РОМе для вычисления чексуммы занимает пару десятков кадров. Да и алгоритм проверки чексумм в наших условиях попросту не нужен.

     

    Для чего были нужны эти контрольные суммы? Это был механизм контроля целостности данных на картридже. Чтобы можно было сразу выявить брак при мануфактуринге РОМ-чипов или в лучшем случае, что нарушен контакт между картриджем и приставкой. Вытащил картридж -- продул контакт и готово (; Ну а если красный экран остается, значит он бракованный, и его следует отнести обратно в магазин, обменять на нормальный.

     

    Думаю, несложно понять что в эмуляторах (а большинство из нас пользуются исключительно ими) вероятность таких вот технических деталей нулевая. Не могут данные игры повредиться из-за плохого контакта BIN-файла с эмулятором или брака при записи этого самого BIN-файла. Именно поэтому я всегда убираю проверки сумм из своих творений за ненадобностью.


  13. Эх, я собирался отписаться намного раньше, но как всегда припозднился...

    Иван, поздравляю тебя с релизом твоего первого серьезного проекта, целого музыкального альбома, причем немаленького! Я знаю, как долго ты лелеял эту идею, как долго впоследствии работал над всеми этими песнями, как старался стремился каждую песню отполировать до идеала. Я был свидетелем этого долгого процесса и знаю, сколько было потрачено сил с твоей стороны. И готов сказать, результаты твоей работы впоследствии презвовшли все ожидания.

    Отдельные композиции понравились мне настолько, что я готов был переслушивать их днями. Все эти работы действительно выделялись из всего того, что я слышал на SMD в последнее время, в особенности среди SMPS музыки. Хорошо подобранные инструменты, мощные и выразительные ударные, а также парочка приятных эффектов с модуляцией, которые кстати мало кто делает. Порой даже забываешь, что все это великолепие воспроизводится на старой доброй Сеге, что достойно похвал. Иван, ты сильно развился за последнее время и переводишь SMPS музыку на качественно новый уровень. Отличная работа и еще раз поздравляю с таким замечательным релизом (;

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

    005 - Sonic CD US Plamtree Panic Present
    011 - Sonic Advance 2 XX Zone
    022 - Grandia 2 Final Boss
    023 - Earthworm Jim 2 Loserens Soil SNES Remix
    030 - Megaman ZX Area A
    046 - Monster Iestin Phantasmal Orbin Zone 2
    055 - Marble Countdown (просто шедевр)


    * * *

    Кстати, хотел упомянуть кое-что относительно плеера, о чем Иван забыл в первом посте. Об управлении, хотя уверен, большинство из вас уже во всем разобралось.

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

    И наконец, воспроизведение слота 000 плавно заглушает всю музыку.

    • Лайк 2

  14. До боли знакомая цветовая гамма и особый, неповторимый авторский почерк.

    999, дорогой, ты ли это?

    http://forum.sonic-world.ru/topic/824-ваши-хаки/?p=253127688

     

    Должен сказать, ты растешь. Палитра и играбельность стали неоспоримо лучше, чем в прошлогодней версии (которая была полностью неиграбельна из-за вылета на логотипе СЕГИ).

    • Лайк 1

  15.  

    Хмм... А как сделать проверку: Соник бегает по наклонной поверхности?

     

    Хотелось бы уточнить: эта проверка должна находиться внутри объекта Соника, чтобы каким-либо образом изменить его поведение, или же она выполняется в другом объекте или вообще в какой-нибудь сторонней процедуре игры (например, Resize_GHZ и т.п.)? При программировании под такую сложную архитектуру, как движок игры, важны все такие мельчайшие детали - задачу нужно ставить строго и четко - от этого напрямую зависит ее решение. Невероятно, но иногда при изменении одной маленькой детали в условии задачи ее решение переворачивается с ног на голову, становится кардинально другим. Я много раз с этим сталкивался. Ведь мы имеем дело с весьма тонкой и массивной архитектурой - движком игры.

     

    Поэтому, я бы посоветовал тебе den121 (и не только тебе) поставить четко задачу, а еще от этого развернуть вопрос. Зачем тебе нужна эта проверка? Какую задачу с помощью нее ты хочешь решить? Тогда на вопрос можно будет более точно ответить, и ты получишь больше полезной информации по своей проблеме, а может даже тебе подскажут более легкий и каноничный способ ее решения.

     

    Сейчас мне нелегко будет ответить на такой расплывчатый вопрос, не зная условий задачи, но все же я попробую. Ответ мой тоже будет весьма расплывчат

     

    В байте $26 структуры Соника, к которому можно получить доступ как к $26(a0) внутри объекта Соника, находится угол его наклона. Это один из верных способов проверить, бежит ли еж по наклонной поверхности.

     

    Если Соник стоит на ровной поверхности, значение этого байта - $00.

    Если Соник поднимается на поверхность при беге вправо, этот байт принимает отрицательные значения: $80..$FF.

    Если Соник спускается с поверхности при беге вправо, этот байт принимает положительные значения: $01..$7F.

     

    При беге в обратную сторону по тем же поверхностям ничего не меняется, то есть:

    Если Соник спускается с поверхности при беге влево, этот байт принимает отрицательные значения: $80..$FF.

    Если Соник поднимается на поверхность при беге влево, этот байт принимает положительные значения: $01..$7F.

     

    Для наглядности, открой уровень в SonED2 и нажми клавишу W, чтобы отобразить данные поверхностей. Нажми клавишу R чтобы отобразить/скрыть углы наклона, соотвествующие каждому из блоков на уровне. Ты увидишь те самые значения, которые отправляются в $26(a0), чтобы сообщить Сонику правильный угол наклона при нахождении на том или ином блоке 16х16. Для прямой поверхности значение всегда 00, для остальных - отличное от нуля.

     

    Таким образом, проверку нахождения на наклонной поверхности можно свести к этому:

    	tst.b	$26(a0)			; проверить байт $26 структуры Соника
    	beq.s	@skip			; если он нулевой, пропустить следующий код
    	; <Соник на наклонной поверхности>
    
    @skip:
    

    Однако, перед исполнением этого кода нужно убедится, находится ли Соник на земле, потому что он может быть наклонен и в воздухе, когда только оторвался от наклонной поверхности. И вот здесь решение задачи зависит от того, что ты хочешь и где будет находится код с проверкой. Если внутри объекта Соника, его можно поместисть в процедуру Obj01_MdNormal, которая гарантированно будет работать только тогда, когда он бежит по земле.

     

    Если же код находится вне объекта Соника, то нужно добавить проверку на нахождение на земле и обращаться к Сонику по-другому:

    	move.b	$FFFFD000+$22,d0	; загрузить байт $22 структуры Соника (состояния Соника)
    	andi.b	#%110,d0		; биты 1 и 2 состояния равны 0? (не в воздухе и не прыгает)
    	bne.s	@skip			; если нет, пропустить следующий код
    	tst.b	$FFFFD000+$26		; проверить байт $26 структуры Соника
    	beq.s	@skip			; если он нулевой, пропустить следующий код
    	; <Соник на наклонной поверхности>
    
    @skip:
    
    

    * * *

     

     

    Уровень s3&k слишком сложен для начинающего, лучше начинай с S1..

     

    Это отчасти так. Соник 1 действительно легче для освоения, так как там намного меньше кода и нагромождений поверх движка, таких как режим 2-х игроков, напарник, которому так же как и основному персонажу нужно взаимодействовать с игровым миром - все это усложняет код объектов и саму структуру движка.

     

    Свой вклад еще вносит отсутствие хороших дизасемблов на Sonic 3K. Еще пару лет назад не было даже полноценного дизасембла S3K. Сейчас он есть, хоть и далек от идеала, и не без ошибок, как утверждают некоторые имевшие с ним дело. Возможность полноценно редактировать уровни тоже появилась сравнительно недавно.

     

    Впрочем, агитировать всех против хакинга Sonic 3K я бы тоже не стал. Куда тогда денется весь прогресс? Если у человека есть желание и рвение, почему бы не помочь ему, вместо того, чтобы ворчать по поводу сложности процесса? Кто знает, может из этого выйдет что-то дельное. Даже если нет, в любом случае приобретеся опыт, который поможет в другом деле. В мире и так почти отсутствуют хаки на Sonic 3K, в то время как хаков на Соник 1 так много, что это даже начинает мозолить глаза. Так что я только за то, чтобы хакинг С3К развивался. Пускай рождается нечто новое, уникальное и интересное, а не старое и однообразное.

     

    1. Как изменять титульник?

    Возможно никак.

     

    Зачем отвечать, если не знаешь ответа? Помощи от такого ответа никакой, как и смысла в нем.

     

    Изменить титульник еще как можно, особенно с последним дизасемблом. Все относящиеся к нему файлы: маппинги, арт и палитры бережно собраны в одной папке: General\Title.

     

    Все ASM-файлы в самой папке Title, начинающиеся с "Map", это спрайтовые маппинги. Они легко открываются в SonMapEd.

    В папке Title\Enigma Map находятся сжатые алгоритмом Enigma плановые маппинги. Их возможно редактировать с помощью PlaneEd.

     

    Выйти на код титульного экрана и все других экранов вообще, можно найдя в главном файле с кодом - sonic3k.asm - метки GameModes. Увидишь ты примерно следующее:

    GameModes:	dc.l Sega_Screen		;   0
    		dc.l Title_Screen		;   4
    		dc.l Level			;   8
    		dc.l Level			;  $C
    		dc.l JumpToSegaScreen		; $10
    		dc.l ContinueScreen		; $14
    		dc.l JumpToSegaScreen		; $18
    		dc.l LevelSelect_S2Options	; $1C
    		dc.l S3Credits			; $20
    		dc.l LevelSelect_S2Options	; $24
    		dc.l LevelSelect_S2Options	; $28
    		dc.l BlueSpheresTitle		; $2C
    		dc.l BlueSpheresResults		; $30
    		dc.l SpecialStage		; $34
    		dc.l Competition_Menu		; $38
    		dc.l Competition_PlayerSelect	; $3C
    		dc.l Competition_LevelSelect	; $40
    		dc.l Competition_Results	; $44
    		dc.l SpecialStage_Results	; $48
    		dc.l SaveScreen			; $4C
    		dc.l TimeAttack_Records		; $50
    

    Код титульника начинается с метки Title_Screen. Обрати внимание, что в зависимости от режима игры - Sonic & Knuckles или Sonic 3 & Knuckles далее выполняется разный код и отображаются разные титульные экраны.

    • Лайк 3
  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×