чат
Самые активные пользователи
denisus
 
--illusionist--
 
razanet
 
Dogvill
 
VOLK10201
 
Zahidylin_Marat
 

Статистика
Всего зарегистрированных пользователей: 51
Последний зарегистрированный пользователь: sixgears

Наши пользователи оставили сообщений: 40 в 30 сюжете(ах)
Кто сейчас на форуме
Сейчас посетителей на форуме: 1, из них зарегистрированных: 0, скрытых: 0 и гостей: 1

Нет

[ Посмотреть весь список ]


Больше всего посетителей (79) здесь было Вс Июл 30, 2017 1:39 am
Последние темы
» Конкурс на лучшую игру на Game Maker
Пн Дек 19, 2011 5:05 pm автор Zahidylin_Marat

» Как сделать TDS на GM .
Пн Июл 11, 2011 3:21 pm автор Dogvill

» Физика главного героя.
Пн Июл 11, 2011 3:05 pm автор Dogvill

» возможности обьекта.
Ср Мар 30, 2011 11:15 pm автор denisus

» Мы переехали на новый хостинг !
Вс Фев 13, 2011 1:51 am автор denisus

» Жвачка - полезна или вредна?
Вс Янв 09, 2011 11:08 pm автор razanet

» Новости с YoYo Games / Game Maker 8.1
Вс Янв 09, 2011 11:03 pm автор razanet

» Радар (views)
Вс Янв 09, 2011 10:17 pm автор denisus

» Noobster [самый простой конструктор игр]
Вс Янв 09, 2011 8:56 pm автор --illusionist--

счетчик
monitoring.cs-hlds.ru/add/
CS 1.6 сервера
мы переехали
[size=18pt]Мы переехали на новый хостинг! Ждем вас здесь: [ftp]http://game.oxnull.net сайт[/ftp] [ftp]http://game.oxnull.net/forum форум[/ftp] Удачи в игрострое ![/size]

Как сделать TDS на GM .

Перейти вниз

Как сделать TDS на GM .

Сообщение  Dogvill в Пн Июл 11, 2011 3:21 pm

Немного общей информации
Всем привет ! этой я хочу рассказать, как сделать на игровом движке Game Maker стрелялку с видом сверху и на его примере показать некоторые важные аспекты создания игр. Обращаю внимание, что статья зделана для новичков и может быть неинтересна пользователям со стадией по выше. Я использую для написания игр язык GML, встроенный в ГМ и предпочитаю его «кнопкам», т.е. методу drag’n’drop. В большинстве показанных приемов я использую общепринятые, либо наиболее эффективные методы или просто те, которые предпочитаю сам (например существует тысяча и один способ сделать пули, поражающие противников), я постараюсь объяснить, чем один код лучше другого.
Надо также заметить, что я использую в данный момент версию Game Maker 8.0 .
что мы собираемся делать?

Я думаю , что, придя в ГМ, вы уже сделали игру, в которой надо щелкать по шарикам, потом возможно арканоид . Оценив свои силы, можно начинать делать нормальные игры, например жанра ТДС.
Итак, во-первых, что это такое – шутер (стрелялка) с видом сверху. Во-вторых – что нам нужно видеть в игре? Это герой, который может ходить по полю и не может проходить через стены, может стрелять из разных видов оружия и собирать предметы .

Создаем героя

ГМ и создаем новый проект (самая левая пиктограмма в меню вверху – чистый лист). Затем сделаем изображение героя – создаем спрайт (пиктограмма с красным Пакменом) и либо загружаем его извне, либо рисуем в редакторе. Для начала можно обойтись простеньким кружочком, красивости мы сделаем потом. Нарисовав спрайт, надо определить его центр – при указании координат рисования центр спрайта придется ровно на эту точку, вокруг нее же будет и вращение. К счастью, за нас придумали кнопку Center, которая выставит центр (Origin) в середину спрайта. Также спрайт надо назвать правильно, чтобы впоследствии к нему было удобно обращаться. Для того чтобы отличать разные типы ресурсов, используются префиксы s_ - для спрайтов и звуков, o_ - для объектов, b_ - для фоновых изображений, f_ - для шрифтов. Именно такие префиксы использую я, но никаких стандартов нет, можете придумывать свои. После префикса обычно идет название ресурса, показывающее, для чего он нужен, в нашем случае это будет s_hero.
Теперь надо создать новый объект, это кнопка с синим кружком вверху. Многие плохо понимают, чем объект отличается от спрайта, я объясню. Спрайт – это просто статичная или анимированная картинка, которая постоянна и не выполняет никаких действий, а объект – это основная единица игры, для которой можно прописать произвольное поведение и назначить его графическую оболочку – спрайт. Для описания поведения объекта у него создаются события – моменты совершения действий, а в них помещаются непосредственно действия, которые объект каждый раз выполняет. Основные используемые события – это:
Create (срабатывает один раз при создании объекта, тут обычно задаются его начальные параметры)
Step (тут действия, которые надо выполнять каждый шаг, постоянно)
Draw (как видно из названия, действия отрисовки)
Collision (столкновение с заданным другим объектом)
События нажатия на определенную кнопку мышки или клавиатуры (Mouse и Keyboard соответственно).
Извиняюсь за большое количество информации, но это важно. Итак, создаем объект героя, называем его o_hero и назначаем ему наш спрайт s_hero. Теперь научим его ходить. У объектов есть большое количество встроенных переменных, в частности x и y – его координаты в комнате, они считаются в пикселях от верхнего левого угла. Если их изменять, то объект перемещается. Поэтому создадим события нажатия кнопок и определим его перемещение. Нажимаем Add Event – Keyboard – Letters – W, затем аналогично A, S и D, это стандартное управление в шутерах. Заходим в первое событие (букву, например W) и создаем в ней действие. Так как мы будем писать действия кодом, то справа выбираем вкладку Control, на ней есть пиктограмма с листиком бумаги, подписанная Execute Code, мышкой перетягиваем ее в поле Actions, откроется редактор кода. В нем просто пишем:

y-=5

Что значит отнять от переменной y 5, то есть сдвинуть героя на 5 пикселей вверх. Это выражение эквивалентно y=y-5 Соответственно делаем и для остальных трех кнопок, используя y+=5, x-=5 и x+=5.
Теперь надо создать нашему герою поле для движения. Для этого создаем новую комнату – кнопка – белый прямоугольник рядом с созданием объекта. Мы получаем наш «уровень», по которому будет ходить герой и в котором можно расставить все нужные объекты. Слева выбираем вкладку Objects, щелкаем под ней левой кнопкой и в списке выбираем нашего героя, левой кнопкой ставим на поле. Теперь можно проверить, как это работает. Для этого нажимаем F5 или кнопку с зеленой стрелочкой на верхней панели. Можно побегать героем и насладиться своим творением.

Создаем нашу комнату
Бегать по пустому полю герою одиноко, поэтому сделаем ему стены. Создаем новый спрайт – черный квадратик 16 на 16 пикселей (здесь его внешний вид не играет роли) и называем его s_wall. 16х16 – это стандартный размер клетки в ГМ, он просто удобен. Также в спрайте стенки надо снять галочку Transparment, иначе она останется прозрачной. Теперь создаем объект o_wall, назначаем ему спрайт и в свойствах объекта ставим галочку Solid – она показывает, что объект «твердый». Само по себе это свойство ничего не дает, но важно в некоторых проверках. Теперь можно выстроить уровень из этих стенок, но пока что герой может через них свободно ходить. Чтобы этого избежать, надо организовать движение героя немного по-другому. Удалите все ранее использовавшиеся события нажатия на клавиши у героя и добавьте событие Step, не End и не Begin, а просто Step.
Введем первые переменные. Некоторое пояснение – в ГМ переменные имеют всего 2 типа – это double и string. Первый – это просто число, может быть положительным, отрицательным, целым и дробным. Второй – это строка, последовательность символов. Специально тип переменной нигде задавать не надо, надо только помнить, какого типа переменную ты используешь. Чтобы задать переменную просто пишем Переменная=значение (число или строка). Итак, введем переменные, которые показывают, на сколько и куда смещается наш персонаж, назовем их xmove и ymove (названия начинаются только с буквы, могут использовать только буквы, цифры и знак подчеркивания _). Также введу понятие условия – оператор if, он проверяет выражение, стоящее после него и делает одно действие, если оно истинно и другое, если оно ложно, действия могут быть блоками. В общем виде это выглядит как

if (variable<10)
{

}
else
{

}

На деле многие формальности можно опускать, например фигурные скобки не нужны, если выполняется только одно действие, а круглые – если есть только одно условие. Оператор else тоже можно опускать, если при невыполнении условия не надо делать ничего. Оператор if проходит, если выражение после него больше или равно 1, для этого различные функции проверки возвращают 0 или 1 как результат. Мы будем использовать функции проверки нажатия клавиши. Пишем в событие Step следующий код:

xmove=0
ymove=0
if keyboard_check(ord('W')) ymove-=5
if keyboard_check(ord('A')) xmove-=5
if keyboard_check(ord('S')) ymove+=5
if keyboard_check(ord('D')) xmove+=5

хоть я это уже писал ну ладно .

Функция keyboard_check проверяет, нажата ли клавиша с указанным номером, а функция ord возвращает номер клавиши с указанным символом. После выполнения этого куска кода в переменных останутся значения сдвига героя. Но мы хотим, чтобы герой сдвигался только в тех случаях, когда в нужном направлении нет стены. Поэтому после предыдущего куска ставим

if not instance_place(x+xmove,y,o_wall) x+=xmove
if not instance_place(x,y+ymove,o_wall) y+=ymove

Здесь функция instance_place пробует поместить текущий объект в указанные координаты и возвращает 1, если там есть столкновение с указанным объектом, или 0, если нет. Оператор not превращает 0 в 1 и наоборот, то есть условие проходит только если столкновения нет. Оператор not можно заменять символом ! , который пишется перед выражением без пробела. Итак, эти условия перемещают героя только если в его новой позиции не будет столкновения со стеной. Я использую 2 проверки, чтобы добиться «гладкости» стен – то есть уткнувшись в стену герой будет идти вдоль нее. Теперь можно действительно построить уровень и ходить по нему героем.
Правда, в этом движении есть еще один небольшой недостаток – если на пустом пространстве зажать кнопки, например влево и вверх, то герой будет смещаться по 5 пикселей в оба направления, а его суммарная скорость возрастет почти в полтора раза (а точнее в корень из двух раз). Эта проблема исправляется очень просто – между двумя предыдущими кусками кода вставьте следующий:

if xmove!=0 and ymove!=0
{
xmove*=0.71
ymove*=0.71
}

Это работает следующим образом – если выполняются оба условия (оператор and), то есть есть смещение по обеим осям, то оба смещения делятся на корень из двух. А выражение один делить на корень из двух имеет всегда постоянное значение 0.71 до 3 значащих цифр. Тот факт, что мы не пересчитываем его каждый раз является примером оптимизации – ускорения работы кода.
Конечно же, черные стены нравятся далеко не всем, поэтому вы можете нарисовать любые стены, какие вы захотите, создать для каждой отдельный объект, поставить галочку solid и в свойствах объекта в поле Parent выбрать объект o_wall. Это делает изначальную стенку «родителем» всех остальных, и все действия, совершаемые над объектом o_wall, в том числе и проверка столкновений совершаются одновременно и над всеми его дочерними объектами.

А теперь научим нашего героя стрелять
Сначала вспомним, как игрок стреляет – он наводит курсор-прицел на цель, нажимает кнопу мыши и из него вылетают пули про это писал урок fozzie. Делаем.
Создаем новый спрайт для прицела – какой-нибудь крестик или кружочек, при этом ориджин надо поставить в нем на ту точку, которая должна соответствовать позиции мышки. Называем его, например, s_prizel (это единственное мое исключение – слово написано транслитом, а не по-английски, потому что английское тяжело здесь подобрать. Хотя, как я уже говорил, название – дело удобства).
Теперь создадим новый объект o_controller, он нам понадобится в основном позже, но сейчас он будет рисовать прицел. В свойствах объекта ставим в окошке Depth значение минус 999 (-999), это значение задает «глубину» прорисовки, определяет, в каком порядке объеты будут рисоваться и перекрывать друг друга. Сначала рисуются объекты с наибольшим значением, потом в порядке уменьшения, то есть наш контроллер будет перекрывать все остальные, это нам понадобится позже. Спрайт для него мы назначать не будем, потому что спрайт автоматически рисуется, если в событии Draw пусто, а мы поставим там свои действия. Создаем это событие у контроллера и пишем там:

draw_sprite(s_prizel,0,mouse_x,mouse_y)

Эта функция, как видно из названия, рисует указанный кадр спрайта в указанных координатах. Так как кадр прицела у нас один, то пишем номер кадра 0. А во встроенных переменных mouse_x и mouse_y хранятся координаты мыши в комнате, они меняются автоматически, поэтому менять их нельзя, только использовать.
Теперь сделаем объект пули. Создаем спрайт s_bullet – черный квадратик 2 на 2, снимаем прозрачность, ставим ориджин в 1,1. Создаем объект o_bullet. Движение мы ему будем задавать при создании, так что нас интересует только уничтожение. Создаем событие Collision, выбираем в списке объект стенки, также создаем событие Other -> Outside Room. В обоих событиях пишем:

instance_destroy()

Эта функция немедленно уничтожает образец, который сейчас обрабатывается.
Теперь учим героя стрелять. Нам понадобится таймер, чтобы он не мог стрелять слишком часто, поэтому в Create героя пишем

canshoot=0

Эта переменная будет отвечать, может ли он стрелять, и стрелять он будет только если она меньше единицы. Теперь добавим в Step героя следующие две строчки (можно в конец или в начало, здесь разницы нет):

direction=point_direction(x,y,mouse_x,mouse_y)
if canshoot canshoot-=1

Первая строчка установит герою правильное направление, чтобы он «смотрел» на позицию мышки. direction – это встроенная переменная, а функция point_direction возвращает направление от точки с перыми координатами к точке со второй парой координат, в данном случае от героя к мышке. Важно запомнить, что в ГМ направление считается в градусах, причем нулевое – это строго вправо, а увеличение от него идет против часовой стрелки, т.е. 90 – это вверх, 180 – влево, 270 – вниз. Вторая строчка уменьшает счетчик. Теперь непосредственно стрельба. Создаем герою событие Mouse -> Global mouse - > Global left pressed. Оно будет срабатывать в момент нажатия левой кнопки мыши, как в пистолете, одно нажатие – один выстрел. В нем пишем:

if canshoot<1
{
canshoot=5
with instance_create(x,y,o_bullet)
{
speed=10
direction=other.direction
}
}

Здесь мы проверяем положение счетчика, если оно подходящее, то ставим сначала новое положение, то есть отсрочку до следующего выстрела в шагах (нормальная скорость игры – 30 шагов в секунду, то есть 5 шагов - это 1/6 секунды). Затем мы функцией instance_create создаем пулю, функция возвращает нам уникальный идентификатор нового образца, который мы используем в конструкции with. Она позволяет выполнить кусок кода для другого объекта, что бывает очень полезно. Затем мы устанавливаем скорость пули (измеряется в пикселях в шаг) и ее направление. Обратите внимание, что направление пуля берет у героя через other., это важный прием. В объекте мы можем обращаться не только к непосредственно его переменным, но и к переменным других объектов. Для этого мы можем писать название объекта, потом точку, потом переменную нужного объекта. Специальное слово other служит либо для обращения к объекту, вызвавшему конструкцию with изнутри самой конструкции, либо в событии столковения для обращения ко второму участвующему в столкновении объекту. Теперь можно и немного пострелять по стенкам. Только вот почему-то пули летят только прямо. Сейчас мы это поправим, для этого в коде выше в присвоении направления поставьте:

direction=other.direction-5+random(10)

Функция random(x) возвращает случайное число от 0 до x, что очень часто используется в играх. Здесь я отнял число, а потом прибавил случайное от вдвое большего, что дает равные шансы на отклонение пули влево или вправо.
А теперь посмотрим на еще один любопытный глюк – если в присвоении скорости ставить значения все больше и больше, то в какой-то момент пуля начнет пролетать через стены. Это происходит из-за того, что пуля каждый шаг перемещается на сколько-то пикселей, но не проверяет линии между своей начальной и конечной позицией, соответственно не замечает стен. Так что же, делать скорость пули маленькой? Нет, есть выход лучше – умные люди придумали метод делать «Мгновенные пули», которые могут лететь с произвольно большой скоростью, не пролетая препятствий. Есть несколько способой сделать это, но я пользуюсь следующим – пули надо вручную, не автоматически перемещать на сколько-то пикселей несколько раз за шаг и останавливать, как только они наткнутся на препятствие.
Сначала создадим объект o_pa_obstacle, префикс pa_ будет означать, что он родительский. Поставим его как родителя объекту o_wall, а потом и остальным объектам, при столкновении с которыми пуля будет останавливаться. При этом все наследники стены будут наследниками и этого объекта, это как дерево. Теперь перепишем присвоения пуле параметров следующим образом:

sp=40
direction=other.direction-5+random(10)
xshift=lengthdir_x(5,direction)
yshift=lengthdir_y(5,direction)

Мы используем переменную sp для обозначения скорости для того, чтобы избежать автоматического смещения пули. Затем условимся, что будем смещать пулю кусочками по 5 пикселей, этого вполне достаточно, чтобы она не проходила через стены толщиной в 16 пикселей и больше. А вот с xshift и yshift уже интереснее – они будут отвечать за единичный сдвиг по каждой из осей, чтобы не пересчитывать его очень часто. Функции lengthdir_x/y очень полезны, они возвращают проекцию вектора длиной в первый аргумент и направлением во второй аргумент на каждую из осей (вспоминаем геометрию). По сути своей, lenghtdir_x(len,dir)=cos(dir)*len, а lenghtdir_y(len,dir)=-sin(dir)*len (это запоминать не обязательно, просто объяснение). Но в ГМ по неизвестным причинам эти функции работают быстрее, чем просто синус или косинус, поэтому лучше пользоваться ими. Желательно делать скорость кратной единичному смещению, это немного упрощает жизнь. А теперь надо написать кусок кода, который циклом несколько раз будет двигать пулю. Движение пули мы организуем циклом for, это хороший шанс его объяснить. Типично он выглядит так: for(i=0;i<10;i+=1) {…}. В скобках всегда идет три выражения. Первое – присваивание начального значения счетчику, в качестве которого обычно используют временную переменную i, второе – это условие продолжение цикла, третье – изменение счетчика, после чего идет блок с действиями цикла. То есть сначала он присваивает переменной значение (это происходит один раз), затем проверяет условие. Если оно проходит (значение 1 или больше), то он выполняет блок действий, затем изменяет счетчик (выполняет третье действие в скобках), затем снова проверяет условие и так далее, пока оно не станет ложным, тогда цикл прекращается. Такой цикл используется, когда действия в скобках как-то зависят от счетчика (например координата рисования кратна ему). А для пули мы используем его следующим образом – в Step пули:

for (i=0;i<=sp;i+=5)
{
x+=xshift
y+=yshift
if instance_place(x,y,o_pa_obstacle) break
}

Это работает следующим образом: мы повторяем цикл, каждый раз добавляя к счетчику по 5, пока он не достигнет нужной скорости (количества перемещений за шаг). В самом цикле мы сначала перемещаем пули на ранее вычисленные величины смещения, а потом принудительно проверяем новую позицию на столкновение с объектом препятствия (то есть со всеми стенками, и позже с врагами). Если столкновение действительно есть, то мы используем функцию break, которая немедленно останавливает цикл, то есть пуля остается внутри препятствия, за чем следует уже автоматическая проверка столкновения – в столкновении со стеной она просто уничтожается, а врагу при этом еще и урон наносит.

разнообразим его вид оружия
В целом, герой уже может ходить и стрелять, можно добавить пару мелочей, например выбор оружия. По сути огнестрельное оружие все действует одинаково – оно выпускает пули, просто они летят с разной скоростью, разбросом и промежутком между выстрелами. Я расскажу общий метод для 2 видов огнестрельного оружия, дальше доделать новых уже просто. Надо учесть, что в таких шутерах обычно какой-нибудь пистолет есть с самого начала, и патроны к нему бесконечные, а впоследствии можно найти новое оружие с ограниченным количеством выстрелов. Пусть у нас переменная героя weapon отвечает за то, какое оружие у него выбрано. Например, 1 – пистолет, 2 – дробовик и т.д. Также для дробовика нам понадобятся переменные have_shotgun и shotgun_patrons – соответственно есть ли у нас дробовик и сколько к нему патронов. Изначально weapon равна 1 (у нас есть только пистолет и он в руках), а остальные две по нулям – дробовика у нас нет. Переключение оружия поставим на кнопки 12345… и так далее. То есть в событие нажатия кнопки 1 ставим просто:

weapon=1

А в двойку чуть сложнее:

if have_shotgun weapon=2

Теперь научим героя подбирать предметы, и заодно ограничим максимум патронов к дробовику, например числом 40, для этого добавим ему в создание переменную shotgun_patronsmax и установим ее значение в 40. Создадим спрайт и объекты дробовика s_shotgun и o_shotgun, затем в событие столкновения героя с дробовиком поставим:

if have_shotgun=0 or shotgun_patrons<shotgun_patronsmax
{
with other instance_destroy()
have_shotgun=1
shotgun_patrons=min(shotgun_patrons+5,shotgun_patronsmax)
}

Работает это так: if проходит если выполняется хотя бы одно из двух условий или оба сразу – у нес нет дробовика или патронов можно взять еще. Тогда герой уничтожает объект дробовика (обратите внимание – к конструкции with относится только функция уничтожения, потому что последующие действия не обрамлены в скобки), затем устанавливает, что дробовик у него уже есть и добавляет себе к нему 5 патронов, здесь работает функция min – к ней можно дописать до 16 аргументов, и она возращает меньший. То есть если у нас было 39 патронов, а мы взяли еще 5, то будет больше 40, поэтому их станет всего 40. Аналогично работает и функция max, только возвращает она наибольший аргумент. Для подбирания коробки с патронами создайте ей спрайт и объект, сделайте такое же событие столкновения, но уберите из условия проверку на наличие дровобика и or, а также присвоение значения переменной дробовика. Тогда если дробовик есть, то мы получаем дополнительные патроны, если нету, то просто патроны, но без дробовика мы ими стрелять не можем. Теперь сделаем стрельбу из нового оружия. Заменим код в событии нажатия кнопки мыши на следующий:

if canshoot<1
switch weapon
{
case 1:
{
canshoot=5
with instance_create(x,y,o_bullet)
{
sp=40
direction=other.direction-5+random(10)
xshift=lengthdir_x(5,direction)
yshift=lengthdir_y(5,direction)
}
break
}
case 2:
{
if shotgun_patrons
{
canshoot=20
shotgun_patrons-=1
repeat(5)
with instance_create(x,y,o_bullet)
{
sp=25
direction=other.direction-15+random(30)
xshift=lengthdir_x(5,direction)
yshift=lengthdir_y(5,direction)
}
break
}
}
}

Здесь мы впервые используем полезный механизм switch-case. В общем виде он выглядит так:

switch variable
{
case 1:
{

break
}
case 2:
{

break
}

}
А работает так – он вычисляет значение variable, затем по очереди сравнивает его с каждым из значений после слов case, которых может быть сколь угодно много. Если значение совпадает, то он выполняет следующий за словом case блок кода, в конце которого желательно ставить break, так как обычно может быть выбрано только одно значение. Если же вам надо добавить действия, которые происходят, если ни одно из значений не подошло, то после последнего case добавьте default: {…}, то есть действие «по умолчанию». Здесь оно не нужно, так как у нас всегда выбрано хоть одно оружие. Хотя можно в специальной ситуации отобрать у игрока все оружие и поставить weapon=0, тогда стрелять он не сможет. Значит, теперь мы разделили код стрельбы для каждого вида оружия – для пистолета он остался тем же, для дробовика немного поменялся. Иногда встречается конструкция repeat(n) {…}, которая просто выполняет кусок кода n раз. Здесь мы создаем несколько дробинок-пуль. Также перед тем, как стрелять, мы проверяем, есть ли у нас патроны к дробовику, а если есть, то отнимаем один. Обратите внимание, что внутрь конструкции повторения надо ставить только действия, связанные с самой пулей, чтобы у нас не отнималось по 5 патронов за раз.
Теперь вы можете сделать себе сколько угодно видов оружия. Чтобы сделать пушку автоматической и стрелять очередями, создайте для стрельбы второе событие Global Mouse Left, туда перенесите обработку стрельбы из автоматического оружия.
Вот и все ! Думаю всем все понятно , УДАЧИ И не кометируйте шоя копирую из других сайтов у мене вси уроки по game maker в текстовом блокноти я их зариние подгатавливаюSad

Dogvill

Сообщения : 2
Репутация : 0
Дата регистрации : 2011-07-11
Возраст : 20
Откуда : Костополь

Посмотреть профиль http://fansaitestalker.ucoz.ua/

Вернуться к началу Перейти вниз

Вернуться к началу


 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения