DECORATE ДЛЯ ЧАЙНИКОВ

ver. 1.54

by Jekyll Grim Payne aka Ashigaru aka zer0

 

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

 

Внимание!

 

Документ гарантированно корректно отображается в Internet Explorer. В Opera нередко смещаются строки, хотя это нередко исправляется установкой масштаба 80-90%.

 

 

Содержание

 

ВВЕДЕНИЕ

            Общая структура WAD

LUMPS

            SNDINFO

         KEYCONF

         DECALDEF

DECORATE

                        Основы DECORATE

                   Спрайты

                   Стейты

                   Основы управления акторами

                   Проперти акторов

                   Флаги

                   Команды

                   Классы акторов

                                   PlayerPawn

                                   Inventory

                                   CustomInventory

                                    Health

                                   HealthPickup

                                   PowerupGiver

                                   Ammo

                        Проджектайлы

                   Weapon

                                   Оружие с вылетающими гильзами

                                               Оружие с перезарядкой

                                               Оружие и усиленное оружие с эффектом TomeOfPower

                                               Оружие, собираемое из нескольких частей

Монстры будет добавлено

FAQ

 

 

 

 

ВВЕДЕНИЕ

<<СОДЕРЖАНИЕ>>         

 

Ваш покорный слуга расчитывает на то, что Вы знаете, что такое Doom и что такое WAD, а также что такое порты Дума. DECORATE предназначен для ZDoom  и GZDoom.

Я работаю с GZDoom, и моя текущая версия – 1.1.0.

 

Для редактирования DECORATE и некоторых других элементов ВАДа мы будем использовать eXtendable Wad Editor (XWE), потому что этот способ – самый толковый. Версию 1.16 можно скачать например здесь: http://ca.geocities.com/xwe@rogers.com/xwe116.zip

Также в работе с ВАДами нам будет помогать сайт ZDoom.org, точнее его подраздел ZDoom Wiki (ZW), содержащий описания всех возможных фич, в том числе и всех команд/параметров/флагов Декорейта для ZDoom и GZDoom. В тексте содержатся гиперссылки на разные страницы ZW.

 

Важно знать, что порты ZDoom и GZDoom постоянно обновляются, в связи с чем изменяются возможности лампов, правила написания какх-то команд, поэтому модификации Декорейта и других лампов могут работать в ранних версиях портов и не работать в поздних, или наоборот. Какие-то команды теряют свои свойства и заменяются расширенными другими, и уследить за всем этим трудно. Вам стоит регулярно проверять ZW, где все вещи, которые перестали работать в новых версиях портов, отмечаются флагом deprecated.

 

 

ОБЩАЯ СТРУКТУРА WAD

<<СОДЕРЖАНИЕ>>         

 

Когда мы открываем Вад через XWE, он оказывается поделен на вкладки. Для начала их мы и разберем.

 

ALL – здесь все вповалку, в том числе и Лампы (Lumpnames), к которым относится и Декорейт, но их мы разберем отдельно ниже.

LUMPS – здесь отдельно Лампы, текстовики, описывающие многочисленные вещи в Думе.

GFX – отдельный раздел картинок, в который входят картинки экранов меню в Думе, картинка статусбара, картинки шрифтов и т.п.

SPRITES – здесь находятся спрайты, использующиеся для отображения объектов в игре (кроме стен и пола, конечно). Вы можете импортировать сюда нарисованные картинки в bmp формате, indexed colors, но иногда результат может оказаться для вас неожиданным. Рекомендуется не выходить за рамки 320х200. У них есть правила называния (об этом позже).

SOUNDS – звуковые файлы. Мы можем импортировать wav или ogg. Стандартные думовские звуки – это wav  22 khz, 8 bit.

MUSIC – музыка. В оригинальном Думе – это что-то вроде midi (формат mus). Мы можем импортировать midi, wav, mp3, ogg.

MAPS – карты в ваде (если есть). Редактировать их лучше не XWE, а редакторами типа Doom Builder.

TEXTURES – это тоже специальный ламп, но выделяется отдельно из других, здесь описываются параметры текстур. Но самих картинок здесь нет.

PATCHES – а вот сюда импортируются картинки самих текстур. Опять-таки bmp. При добавлении картинки сюда, она автоматически попадает под тем же названием в TEXTURES, но потом вы можете изменить название текстуры (там остается ссылка на патч).

FLOORS – текстуры для пола/потолка. Здесь сами картинки, без деления на текстуры-патчи. Они идут отдельно в связи с особенностями Думовского псевдо-трехмерного движка. Но современные порты и редакторы позволяют использовать текстуры или полы и на стенах, и на полах без ограничений.

 

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

 

Надо принять во внимание, что элементы Вада часто отображаются не в том разделе, в каком надо, и не всегда сразу видно, что это за элемент – звук, графика, музыка (только спрайты и текстуры видно всегда). Например, в SOUNDS отображаются только те звуки, название которых начинается с букв DS – что является сокращением от "Doom Sound", однако мы вольны называть звук как угодно, и тогда нам придется находить его через ALL. Об этом не волнуйтесь, это нормально.

 

 

LUMPS

<<СОДЕРЖАНИЕ>>         

 

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

 

DECORATE

SNDINFO

KEYCONF

DECALDEF

 

Это, конечно, далеко не полный список существующих лампов, но именно с этих надо начинать.

 

SNDINFO

<<СОДЕРЖАНИЕ>>         

 

SNDINFO – это ламп, управляющий звуками. Когда вы в Декорейте или в скрипте карты в редакторе карт назначаете ссылку на звук, вы указываете не сам звуковой файл, а его имя в SNDINFO. Например, вы добавили звук PISTSHOT.wav – появился звук PISTSHOT. Теперь в SNDINFO надо добавить его имя. Выглядит это, например, так:

 

SNDINFO:

weapons/pistol/shot     PISTSHOT

 

Тогда в скрипте или Декорейте вы будете указывать ссылку на "weapons/pistol/shot". Но есть возможность назвать звук как угодно. Просто, согласитесь, удобнее, если вы будете отделять звуки оружия, объектов и т.п.?

Существует способ заставить случайным образом в один и тот же момент звучать разные звуки, хотя ссылка будет указана на один. Допустим, у вас три немного разных звука выстрела из пистолета – PISTSHO1, PISTSHO2, PISTSHO3. Можно сделать так:

 

SNDINFO:

weapons/pistol/shot1    pistsho1

weapons/pistol/shot2    pistsho2

weapons/pistol/shot3    pistsho3

 

$random weapons/pistol/shot { weapons/pistol/shot1 weapons/pistol/shot1 weapons/pistol/shot1 }

 

Теперь при ссылке на "weapons/pistol/shot" будет случайным образом звучать один из трех звуков.

 

 

KEYCONF

<<СОДЕРЖАНИЕ>>         

 

KEYCONF – очень важный ламп. В нем указываются значения клавиш, в том числе можно создать и новые клавиши. Если вы создали новое оружие, то вам как минимум нужно назначть это оружие на клавиши. Выглядит это так

 

KEYCONF

weaponsection "Weaponsectionname"

 

setslot # weaponname

 

 

Здесь:

Weaponsectionname – надо указать до того, как начнете указывать слоты. Просто название латинницей без пробелов. Причем не забудьте про кавычки. Это нужно, потому что (G)ZDoom при подключении вада добавляет для каждого вада дополнительные настройки в ваш конфиг zdoom-Имя пользователя компьютера.ini, который лежит в папке с лаунчером. И, если у вада свои настройки для оружия, лаунчер создаст отдельную секцию под названием "Weaponsectionname" и пропишет в нее все настройки.

# - это цифра, любая от 1 до 0 (включая 2-9, если кто не понял), то есть кнопки, к которым привязывается оружие. Можно использовать и все десять кнопок, если хочется.

Weaponname – имя оружия, назначенное ему в Декорейте (об этом подробнее позже).

 

На кнопку можно привязать сколько угодно стволов, просто разделив названия пробелом. Вот пример из обычного Дума:

 

KEYCONF

setslot 1 Fist Chainsaw

 

Так у нас на цифре 1 оружия Fist и Chainsaw (в данном случае думовские кулак и бензопила). Обратите внимание, что игрок будет доставать первым то оружие, которое указано в строчке последним (если оно у него есть, конечно) – т.е., в данном случае, вначале бензопила, потом кулак (при повторном нажатии той же кнопки).

 

Также вы можете захотеть изменить свойства самого игрока. Игрок – тоже объект Декорейта, именуемый по умолчанию DoomPlayer. Если вы создали другого игрока, например Doomer, то вам придется указать его в KEYCONF таким образом:

 

KEYCONF

clearplayerclasses

 

addplayerclass Doomer

 

Существует возможность создать несколько классов игрока – например, как в Hexen, где их три. Для этого вам просто понадобится создать три объекта типа DoomPlayer (об их создании мы поговорим в разделе про Декорейт), а потом все их прописать в KEYCONF, например так:

 

KEYCONF

clearplayerclasses

 

addplayerclass Fighter

addplayerclass Cleric

addplayerclass Mage

 

Также, возможно, вы захотите создать какие-то новые кнопки с новыми функциями. Это следует делать так:

 

KEYCONF

addkeysection "Keysectionname" name_keysection

 

addmenukey "Menukeyname" command

 

defaultbind keyname command

 

Keysectionname – секция кнопок, она появится в опциях лаунчера под отдельным названием (также, как в самом лаунчере разделены по секциям кнопки перемещения, управления ботами и т.п.).

name_keysection – а это название секции кнопок отразится в конфиге. "_keysection" должно быть обязательно.

Menukeyname – название самой кнопки.

command – команда, описывающее выполняющееся действие. Например "use название" – использовать какой-нибудь предмет, или "fov 120" – изменение ширины вертикального обзора (fov). Или что-то еще. Это все равно что через консоль прописывать бинды кнопок.

Достаточно важно знать команду "puke #" – если вам это не известно, то поясняю, что эта команда активирует скрипт номер #. В некоторых продвинутых вадах это может использоваться в разных целях, например, в ваде "Ultimate Torment and Torture" привязка этой команды к кнопке используется для создания кнопки, отключающей погодные эффекты в ваде.

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

 

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

 

В этом лампе важен регистр, название оружия в слоте должно быть написано с сохранением размера букв оригинального названия.

 

DECALDEF

<<СОДЕРЖАНИЕ>>         

 

Этот ламп управляет следами от выстрелов, брызгами крови и т.п. (декалями). Назначать следы от выстрелов очень просто:

 

DECALDEF

generator Weaponname Decalname

 

 

Здесь все понятно. Например вы создали в Декорейте оружие Handgun и хотите, чтобы оно оставляло следы от пуль на стене.

 

DECALDEF

generator Handgun bulletchip

 

 

bulletchip – один из уже существующих в (Г)ЗДуме декалей. К ним также относятся scorch, bigscorch, bfgscorch, причем некоторые из них анимированы и цветные.

Обычные декали вы можете прописать в свойствах оружия и прямо в Декорейте. Однако вы не сможете обойтись без DECALDEF, если хотите создать свой декаль. Делается это так:

 

DECALDEF

decal Decalname

{

[код]

}

 

 

Существует набор параметров, которые можно использовать для описания декаля. Здесь мы воспользуемся помощью ZW:

 

pic picname – картинка с именем picname будет использоваться для декаля, это либо спрайт, либо картинка класса GFX (в соответствующей вкладке в XWE, это все равно bmp)

shade "RR GG BB" – изменение цвета декаля по RGB (значения от 0 до 255)

x-scale #.# - изменение растянутости картинки декаля по оси X на #.# (по умолчанию 1.0, меньше – уменьшить, больше – растянуть)

y-scale #.# - то же, только для оси Y

flipx – картинка декаля отразится по оси X при появлении

flipy – то же для оси Y

randomflipx – картинка может отразиться по оси X при появлении, а может и не отразиться

randomflipy – то же для оси Y

solid – декаль будет непрозрачным

translucent #.# - прозрачность декаля (0.0 – прозрачный, 1.0 – полностью видимый)

add #.# - дополнительная прозрачность (зачем – не знаю)

fuzzy – декаль будет "невидимым" (как невидимый Пинки (Спектр))

fullbright – декаль будет иметь полную яркость картинки, независимо от освещенности комнаты, где он появился

lowerdecal decalname – декаль decalname автоматически нарисуется под указанным декалем при его появлении

animator – включает использование Decal Animator (я этим не заинтересовался, заинтересованным по ссылке)

 

Итак, пример простого декаля:

 

DECALDEF

decal MyDecal

{

       pic SCORCH1 //использует картинку SCORCH1

       shade "00 10 00" //слегка зеленоват

       x-scale 0.25 //уменьшен вчетверо по X

       y-scale 0.25 //уменьшен вчетверо по Y

       randomflipx //имеет шанс отразиться по X

       randomflipy //имеет шанс отразиться по Y

}

 

generator Weaponname MyDecal //Оружие Weaponname использует декаль MyDecal

 

Вот так выглядит декаль. Описание я дал после знака "//" – все, что пишется после двойного слеша считается комментарием и не является частью кода. Заметьте, что я уменьшил большую картинку декаля – засчет этого он сам получится высокого разрешения.

Декаль можно сделать так, чтобы картинка выбиралась рандомно:

 

DECALDEF

decal Decalname1

{

[код]

}

 

decal Decalname2

{

[код]

}

 

decal Decalname3

{

[код]

}

 

 

decalgroup Decalgroupname

{

       Decalname1 1

       Decalname2 1

       Decalname3 1

}

 

generator Weaponame Decalgroupname

 

Здесь декаль Decalgroupname будет показывать один из декалей: Decalname1, Decalname2, или Decalname3. Вариантов можно сделать сколько угодно. А оружие Weaponname будет создавать декаль Decalgroupname.

 

В этом лампе важен регистр, название оружия и декаля в генераторе должно быть написано с сохранением размера букв оригинального названия.

 

Декали можно назначать без DECALDEF, прямо в Декорейте в свойствах оружия.

 

 

 

DECORATE

<<СОДЕРЖАНИЕ>>         

 

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

 

 

Основы DECORATE

<<СОДЕРЖАНИЕ>>         

 

Декорейт – такой же текстовый ламп, как и остальные. Декорейт управляет всеми объектами в Думе – игроком, монстрами, оружием, предметами интерьера. Изначально, на ранних стадиях разработки ЗДума, предполагалось, что Декорейт будет управлять только декорациями, отсюда и такое название лампа.

Любой объект в Декорейте называется ACTOR – с этого слова и будет начинаться описание любого объекта.

Теперь рассмотрим базисы кода. Для примера возьмем шаблон актора:

 

DECORATE

Actor Actorname : Actorclass

{

[код]

}

 

 

С именем актора правила знакомые – латинница и цифры, без пробелов.

После знака двоеточия указывается класс актора. Или же может не стоять ничего. И есть другие варианты.

 

 

Спрайты

<<СОДЕРЖАНИЕ>>         

 

Далее стоит принять во внимание, что подавляющее большинство объектов Декорейта имеют какой-нибудь внешний вид. В этой связи надо знать, как его создавать.

 

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

1) Спрайты любых объектов в пространстве

2) Спрайты экранного оружия

3) Спрайты, использующиеся в hud (например, картинки лежащих в инвентаре предметов)

 

Спрайт – это картинка по типу bmp, ее можно импортировать в вад с помощью XWE. Существуют определенные правила для ИМЕНИ спрайта.

Имя спрайта всегда имеет следующий вид:

 

XXXX$#

 

XXXX – это любые четыре буквы или цифры, собственно, именно это и есть имя спрайта, оно состоит из 4-х символов, не больше, не меньше

$ - это обозначение кадра, его нужно обозначать буквой

# - это номер варианта кадра, соответственно точки, с которой наблюдается объект

 

Возьмем для примера спрайт игрока:

 

PLAYA1

 

PLAY – имя спрайта, понятное дело, отсылка к слову "player"

A – обозначение кадра, это первый кадр, где игрок стоит, слегка выдвинув левую ногу вперед

1 – это варианта кадра A, когда игрок виден спереди

 

К нему в компании идут еще четыре спрайта:

 

PLAYA2A8

PLAYA3A7

PLAYA4A6

PLAYA5

 

Это все – варианты все того же кадра A, только с разных сторон. Спрайты игрока и монстров в Думе восьмисторонние, но у большинства монстров и у игрока спрайты охватывают только пол-круга, а на другую половину круга идут отражения спрайтов. Поэтому:

 

PLAYA2A8 – тот же кадр с выдвинутой вперед левой ногой, но повернутый на 45 градусов влево/вправо (это одновременно A2 и A8, отсюда такое название)

PLAYA3A7 – тот же кадр, но повернутый на 90 градусов влево/вправо

PLAYA4A6 – тот же, но на 135 градусов влево/вправо

PLAYA5 – тот же, но со спины (как вы понимаете, кадр со спины нельзя использовать в отражении, иначе объект будет выглядеть одинаково и спереди, и сзади)

 

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

 

Как вы уже могли догадаться, лежащие предметы обычно обозначены одним единственным спрайтом – аптечка например. У них цифра равна 0:

 

MEDIA0 – так выглядит название спрайта большой аптечки в Думе

 

Но можно сделать и много картинок – чтобы аптечка выглядела по-разному с разных сторон. Это уже ваше личное дело.

 

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

 

Надо сразу заметить, что в (Г)ЗДуме уже есть два спрайта – пустые спрайты, прозрачные. Это NULLA0 и TNT1A0. Надо иметь в виду, что NULLA0 НЕЛЬЗЯ ИСПОЛЬЗОВАТЬ В ПЕРВОМ КАДРЕ АНИМАЦИИ ОБЪЕКТОВ, только в анимации экранного оружия. Если использовать его в анимации объекта (во всяком случае, если использовать его первым кадром), объект будет вести себя так, будто у него неправильно указан спрайт (в игре он будет выглядеть как желтый ромб с надписью "no image" и ничего не будет делать). Если вам нужен пустой спрайт у объекта, делайте это через TNT1A0. Но NULLA0 можно использовать в анимации оружия на экране.

 

Спрайты, использующиеся в hud проще. Там можно позволить себе вольность и пренебречь некоторыми правилами, потому что спрайты в hud неанимированные и не имеют сторон обзора, поэтому там не надо обозначать кадр и сторону. Вы можете просто дать спрайту любое название, не более 8 символов, например BIGFLASK.

 

 

Стейты

<<СОДЕРЖАНИЕ>>         

 

Но теперь надо разобраться, как собственно создавать внешний вид. Для этого в коде акторов используются Стейты. Выглядит это так:

 

DECORATE

Actor Actorname : Actorclass

{

   [код, обозначающий разные параметры и флаги актора]

   state

   {

      [код, описывающий стейты]

   }

}

 

 

Заметьте, что код стейтов выделяется в отдельную пару фигурных скобок – НИКОГДА не забывайте об этом.

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

 

DECORATE

actor Column 2028 //номер здесь обозначает индивидуальный id актора, но новым объектам мы можем его не давать

{

  spawnid 155 //это так называемый spawn number, его можно использовать в скриптах спаунящих итемы (стандартные акторы тоже имеют свои spawn numbers)

  radius 16 //это радиус актора, измеряется в маппикселях, для сравнения радиус Импа равен 20

  height 48 //высота, которая в оригинальном Думе не использовалась, т.к. там объекты были бесконечно высоки; высота Импа - 56

  +SOLID //это флаг, он обозначает, что объект твердый – через него нельзя пройти, но через него летят пули

  states

  {

  Spawn: //это стандартный стейт, в нем описывается внешний вид и поведение актора в стандартном состоянии, когда он только появился на карте

    COLU A 1

    loop

  }

}

 

 

Четыре буквы – имя спрайта, потом пробел, буква обозначающая кадр, пробел, цифра – время показа этого кадра в тиках (1 тик = 1/35 секунды). Loop – команда бесконечного повторения этого кадра. Если бы у нас было несколько кадров, пошла бы повторяющаяся анимация. Например:

 

DECORATE

actor Torch

{

  radius 10

  height 64

  +SOLID

  states

  {

  Spawn:

    TRCH ABCD 2 bright

    loop

  }

}

 

 

Это факел (не какой-то конкретный, а абстрактный, примерно так может выглядеть код какого-нибудь факела). Картинки  TRCHA0, TRCHB0, TRCHC0, TRCHD0 повторяются одна за другой. Причем каждая держится 2 тика. "bright" обозначает дополнительную яркость спрайта Если вам нужно, чтобы число тиков было разное, разделяйте кадры:

 

DECORATE

actor Torch

{

  radius 10

  height 64

  +SOLID

  states

  {

  Spawn:

    TRCH A 2 bright

    TRCH B 4 bright

    TRCH C 3 bright

    TRCH D 2 bright

    loop

  }

}

 

 

Надо заметить, что, если у объекта всего один кадр в спавне, можно обойтись без Loop:

 

DECORATE

actor Column 2028

{

  radius 16

  height 48

  +SOLID

  states

  {

  Spawn:

    COLU A -1

    stop

  }

}

 

 

Если длительность равна -1 тик – значит этот кадр застынет и никуда не исчезнет. Если бы мы поставили stop после кадра длинной 1, он бы показывался в течение 1 тика и исчез. Однако, даже поставив -1, stop все равно надо писать – это как закрывание стейта, его нельзя оставлять распахнутым.

НИКОГДА НЕ СТАВЬТЕ LOOP ПОСЛЕ КАДРА НУЛЕВОЙ ДЛИНЫ, это чревато зависанием. После кадра нулевой длины может быть что угодно, но не Loop.

Другой вариант закрывания стейта – goto statename. В этом случае назначается перенаправление на другой стейт. Об этом подробнее позже.

 

Ну, и самое важное. Новые версии (Г)ЗДума позволяют создавать собственные стейты с любыми названиями. В них можно отсылать через goto и разные джампы (это команды, о них ниже).

 

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

 

 

Основы управления акторами

<<СОДЕРЖАНИЕ>>         

 

Созданный вами актор может замещать уже существующий актор в игре:

 

DECORATE

Actor DarkImp replaces DoomImp

{

...

}

 

 

DarkImp будет появляться в игре вместо обычного думовского импа.

 

А еще актор может копировать проперти, флаги и стейты другого актора – целиком или частично:

 

DECORATE

Actor DarkImp : DoomImp

{}

 

 

В этом случае DarkImp целиком копирует обычного импа. Но, по желанию, можно изменить только какие-то параметры, а другие оставить.

 

DECORATE

Actor DarkImp : DoomImp

{

health 200

}

 

 

Тут DarkImp повторяет обычного импа, но у него 200 хелсов.

 

Однако в обоих вышеупомянутых случаях актор уже не заменяет импа, потому что у нас отсутствует "replaces". Чтобы актор не только копировал свойства другого актора, но и замещал его, надо придать коду такой вид:

 

DECORATE

Actor DarkImp : DoomImp replaces DoomImp

{

health 200

}

 

 

Теперь актор DarkImp заменяет во всех случаях обычного импа. От обычного импа он отличается только количеством хелсов.

 

Это в случае с монстрами. В случае с предметами инвентаря мы еще и должны писать класс актора, например:

 

DECORATE

Actor SuperBoost : PowerupGiver replaces Megasphere

{

[код нового пауэр-апа]

}

 

 

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

 

DECORATE

Actor SuperBoost : Megasphere replaces Megasphere

{

[код нового пауэр-апа]

}

 

 

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

 

 

Проперти акторов

<<СОДЕРЖАНИЕ>>         

 

Это любые простые свойства акторов. Радиус, высота, наносимые повреждения (для монстра – damage #), скорость – это проперти. Это просто текст и цифры или другой текст после них. Их мы частично разберем по ходу, но некоторые нужно знать наизусть:

 

spawnid # - это спаун номер актора (spawn number), через него мы будем вызывать актора, если надо вызвать его скриптом (все стандартные объекты имеют свои номера: Spawn Numbers)

radius # - радиус актора

height # - высота актора

scale # - увеличение спрайта актора в # раз (по умолчанию 1.0), это чисто визуальное увеличение (радиус и высоту при этом надо увеличивать вручную), можно использовать как уменьшение, не работает на экранных спрайтах (спрайтах оружия); если дать какому-то объекту большую картинку в качестве спрайта и уменьшить ее через эту проперти, получится спрайт высокого разрешения

xscale # - горизонтальное растяжение спрайта актора в # раз

yscale # - вертикальное растяжение спрайта актора в # раз

speed # - скорость движения актора (монстра, игрока)

damage # - повреждения, наносимые актором при активации A_BulletAttack в его атакующем стейте (если это монстр), либо повреждения, наносимые актором при соприкосновении с целью (если это снаряд)

meleedamage # - сила ближней атаки актора равна #*random(1,8) (указанное число умножить случайным образом на число от 1 до 8)

health # - здоровье актора (устанавливает прочность любого расстреливаемого объекта, это может быть не только монстр)

mass # - масса управляет уровнем неподвижности актора, чем больше масса, тем меньше актора сдвигают попавшие по нему атаки (по умолчанию 100)

painchance # - это шанс того, актор (монстр, игрок) перейдет в стейт Pain, при этом, если это монстр, то таким образом можно сбить его атаку, а если это игрок, то просто будет показана анимация боли (цифра от 0 до 256)

renderstyle renderstylename – использовать на спрайт актора какой-нибудь рендер (например translucent даст возможность сделать актор полупрозрачным)

alpha #.# - прозрачность актора равна #.# (от 0.0 до 1.0, 0.0 – невидимый), если при этом вы прописали renderstyle translucent

decal decalnameэто оружие использует декаль decalname (если оружие стреляет снарядами, то эту проперти надо привязывать к снаряду)

bouncefactor # - сила, с которой снаряд отскакивает от стен и пола (если у него стоит флаг +DOOMBOUNCE/+HEXENBOUNCE/+HERETICBOUNCE), по умолчанию 0.7

bouncecount # - максимальное число раз, которое снаряд с флагом +DOOMBOUNCE/+HEXENBOUNCE/+HERETICBOUNCE может отскочить от стен/пола, прежде чем уничтожится (по умолчанию бесконечно)

dropitem itemname # - актор имеет # шанс при смерти (на команде A_NoBlocking) выронить итем itemname (шанс от 1 до 256, по умолчанию 256)

seesound "soundname" – увидев цель (по умолчанию игрока), актор (по умолчанию враждебный монстр) издает звук soundname

activesound "soundname" – активировавшись (спугнутый каким-то звуком, например), актор (по умолчанию враждебный монстр) издает звук soundname

attacksound "soundname" – атакуя цель (по умолчанию игрока), актор (по умолчанию враждебный монстр) издает звук soundname

painsound "soundname" – входя в стейт боли (когда по нему попадают), актор (по умолчанию монстр или игрок) издает звук soundname

deathsound "soundname" – умирая, актор (монстр, игрок) издает звук soundname

meleesound "soundname" – атакуя ближней атакой и попадая, актор (монстр) издает звук soundname

obituary "text" – если актор монстр, то текст text появится в игре, когда тот убьет игрока, если актор – снаряд или оружие, то он появится в десматче, когда один игрок убьет этим оружием/снарядом другого

hitobituary "text" – то же самое что и obituary, но используется для оружия ближнего боя.

Использующиеся в obituary специальные обозначения:

- %o – имя убитого

- %k – имя убившего

- %ghe/she/it, в зависимости от пола игрока

- %h – him/her/it

- %p – his/her/its

 

И надо сразу заметить, что есть еще две очень важных проперти. Дело в том, что в Думе монстры и снаряды не выделены в отдельный класс, в отличие от оружия, патронов и т.п. Они становятся таковыми через указывание соответствующей проперти:

 

monster – актор является монстром (атакует игрока)

projectile – актор является проджектайлом (снарядом)

 

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

 

Стоит отметить что, по желанию, во всех случаях вместо цифр можно использовать формулу random(min,max), причем и в проперти, и в командах. Не забывайте о скобках.

 

Полный список проперти можно найти в соответствующем разделе ZDoom Wiki.

 

 

Флаги

<<СОДЕРЖАНИЕ>>         

 

Выше я упоминал флаги. Флаги – это определенные свойства актора (но не путать с Actor Properties, это другое), их можно включать или выключать через +FLAGNAME и -FLAGNAME. Набор возможных флагов, как и возможных стейтов, зависит от класса актора. Существует несколько основных флагов, которые необходимо знать, их мы разберем (все флаги без исключения описаны в Actor Flags).

 

Также напоминаю, что при использовании проперти monster или projectile мы сразу автоматически включаем набор флагов. Например, любой монстр автоматически имеет флаг +SHOOTABLE, и отдельно его включать не нужно.

 

+SOLID – через актора можно стрелять, но нельзя пройти (для монстров отдельно включать не нужно)

+SHOOTABLE – актора можно расстрелять (для монстров отдельно включать не нужно)

+ALWAYSPICKUP – актор (предмет) можно всегда взять, даже если у игрока уже максимально возможное число этих предметов

+AUTOACTIVATE – актор (предмет) автоматически активируется при его взятии (например думовский антирадиационный костюм), этот флаг по умолчанию включен

+INVBAR – если у актора стоит -AUTOACTIVATE и он при взятии идет в инвентарь, а не используется, то с этим флагом взятый предмет еще и будет отмечаться в инвентаре (иконкой управляет проперти inventory.icon "iconname")

+COUNTITEM – этот актор (предмет) учитывается в общей статистике уровня по количеству собранных предметов, этот флаг по умолчанию включен

+COUNTKILL – этот актор (монстр) учитывается в общей статистике уровня по количеству убитых врагов, этот флаг по умолчанию включен у монстров

+FLOAT – монстр с этим флагом может летать и изменять высоту по желанию

+FLOATBOB – актор (предмет) с этим флагом плавает в воздухе вверх-вниз (как бутылки-аптечки в Hexen); не забудьте поднять спрайт актора вверх, чтобы он не нырял под землю при опускании вниз

+NOGRAVITY – актор может висеть в воздухе (монстрам с +FLOAT включать не нужно)

+DOOMBOUNCE – актор отскакивает от стен и пола, через некотрое время теряя ускорение (для гранат, например)

+HEXENBOUNCE – актор отскакивает от стен и пола, не теряя ускорения

+HERETICBOUNCE – актор отскакивает от пола, не теряя ускорения

+PUSHABLE – актора можно толкать (например, можно сделать бочку, вроде думовской, которую можно толкать)

+NOBLOCKMAP – с этим актором не могут соприкоснуться другие, но он с ними может (чтобы было понятнее – посмотрите на поведение думовской ракеты), также, если этот актор под действием гравитации упал на движущийся пол, он не будет двигаться вместе с ним

+FRIENDLY – помогает игроку (можно сделать дружелюбного монстра)

+NOTELEPORT – попав в телепортер не телепортируется

+INVULERABLE – актор неуязвим

+REFLECTIVE – актор отражает от себя снаряды

+NORADIUSDMG – не воспринимает повреждения ударной волны (не получит повреждений от взорвавшейся рядом ракеты)

+NOBLOOD – из актора не течет кровь

+ROCKETTRAIL – актор постоянно испускает ЗДумовский ракетный след (по умолчанию используется на ракету в (G)ZDoom)

+GRENADETRAIL – актор испускает ЗДумовский след от гранаты (похож на дым от гранаты в Quake)

+RIPPER – актор (снаряд) летит сквозь врагов, нанося им повреждения (соответственно своему damage), но сам не уничтожается при этом (уничтожение происходит только об стену)

+BOSS – актор (монстр) является боссом: звуки его активации и смерти играются на полную мощность во всем уровне, на него не действуют предметы из Hexen Banishment Device и Disc of Repulsion и сопротивляется некоторым другим воздействиям, существующим в Hexen

 

Остальные флаги тоже стоит знать, поэтому все-таки загляните в Actor Flags.

Некоторые из флагов можно писать с префиксом. Например, флаг AUTOACTIVATE, который относится только к итемам, можно писать как +INVENTORY.AUTOACTIVATE (подробнее все там же, в Actor Flags), но эти префиксы не обязательны. Не путайте их с аналогичными префиксами в некоторых уникальных проперти акторов – там они обязательны.

Напоминаю, что перед флагом надо ставить +/-.

 

 

Команды

<<СОДЕРЖАНИЕ>>         

 

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

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

 

DECORATE

actor ZombieMan 3004

{

... //я так обозначаю опущенные куски кода, если кто не понял

POSS F 8 A_PosAttack

...

}

 

Указанный A_PosAttack – это стандартная атака Зомби, вписанная в движок, которая заставляет монстра выстрелить одной пулей с мощностью 1 и одновременно издать звук "grunt/attack".

Атаки всех стандартных думовских монстров – это запрограммированные команды, вызывающие только определенные виды атак с определенными звуками и эффектами. Однако, для новых монстров мы можем пользоваться набором свободно регулируемых команд, обладающих сопровождающими их параметрами (которые пишутся в скобках).

Некоторые из этих команд являются общепринятыми, и их надо знать.

 

Команды имеют вид A_command(parameter1,parameter2,…). Исключение составляет небольшое число скриптовых команд, но о них отдельно.

Команды можно разделить на команды "извне" и "изнутри". Первые регулируют действия монстров и разных объектов. Вторые – это действия для вашего оружия.

Основные команды, которые нужно знать:

 

A_BulletAttack(#) – враг стреляет пулей, нанося # повреждений; также в свойствах актора над стейтами можно указать повреждения отдельно в виде damage #. Также издает звук attacksound "soundname". Например:

 

DECORATE

actor Shooter

{

damage 5

attacksound "monsters/shooter/attack"

...

SHTR F 8 A_BulletAttack

...

}

 

A_Look – эта типичная команда монстра, монстр ищет возможные цели в поле своего зрения, но никуда не идет

A_Chase – эта команда прописывается в стейте, управляющем анимацией движения монстра, командует монстру идти к цели

A_FaceTarget – монстр поворачивается к своей цели (по умолчанию – игрок, а если есть флаг +FRIENDLY, то к другим монстрам). Это надо прописывать перед командой самой атаки.

A_CustomMissile("Missilename", spawnheight, Xoffset, Xangle, aimmode, Yangle) – актор (обычно монстр) стреляет проджектайлом (снарядом) Missilename, который появляется на высоте spawnheight от нижнего конца актора, может быть сдвинут по горизонтали от центра актора на Xoffset, может быть выпущен с горизонтальным отклонением Xangle (в градусах) от точки, куда смотрит монстр. В дополнение можно использовать еще два параметра – aimmode и Yangle. Aimmode может быть 0 – снаряд направляется в цель монстра (таково значение по умолчанию), 1 – снаряд автоматически направляется параллельно абстрактному снаряду с высотой спауна 32 и горизонтальном отклоном 0 (смысла я в этом режиме не вижу, хотя его можно использовать для создания монстра, стреляюещго большим числом ракет, но я предпочитаю делать это через код самого монстра) или 2 – в этом случае снаряд воспринимает последний параметр – Yangle – который обозначает вертикальное отклонение (в градусах) от прямой линии, по котрой идет взгляд монстра. Все параметры, кроме названия проджектайла – цифры, причем могут быть и отрицательными, и положительными. Если вы не хотите использовать параметры, пропишите нули, а последние два параметра можно вообще не писать.

A_CustomRailgun (damage, Xoffset, ringcolor, corecolor, silent, aimmode) – монстр стреляет рейлганом (в ЗДуме уже есть прорисовка луча рейлгана, примерно как в Quake 2) с силой damage, сдвигом по горизонтали от центра актора Xoffset, с цветом кольца ringcolor, цветом луча corecolor, со звуком или бесшумный (стоит ли 0 или 1 вместо silent) и с видом наводки aimmode. Цвет можно указать словом в кавычках или без (red, blue, yellow, green, lawngreen, darkred, black, white и, возможно, что-то еще), или написать none, тогда луча/кольца не будет вообще. Бесшумность выстрела регулируется установкой 1 (бесшумный) или 0 (со звуком, звук уже вписан в лаунчер). Aimmode бывает 0 (монстр стреляет куда смотрит) и 1 (монстр стреляет в свою цель).

A_MeleeAttack – монстр атакует атакой ближнего боя, при этом использует проперти meleedamage # (сила атаки) и meleesound "soundname" (звук, звучащий, если атака попала по врагу).

A_CustomComboAttack ("missilename", spawnheight, damage, "soundname", damagetype, bleed)комбинированная атака монстра: он либо стреляет снарядом missilename на высоте spawnheight, либо, если стоит достаточно близко к своей цели, бьет ближней атакой с силой damage, при попадании издает звук soundname, при этом используя тип демеджа damagetype (о типах демеджа подробнее в разделе про проджектайлы) и, если bleed = 1 (по умолчанию 1), то из актора течет кровь при удачном попадании. Этой командой можно создать атаки, аналогичные атакам многих стандартных думовских монстров – например, импа, хелл найта, барона и т.п. – которые издали швыряют снаряд, а вблизи бьют ближней атакой.

A_NoBlocking – монстр становится неосязаемым и роняет все свои предметы (они указываются через проперти монстра dropitem itemname #). Используется в анимации смерти монстра. Если привяжете эту команду к нескольким кадрам, она и выполнится несколько раз.

A_Pain – играет звук, указанный в проперти актора painsound "soundname".

A_Explode(radius,damage,donthurtshooter) – активирует взрыв актора (проджектайла), при котором он издает свой deathsound, радиус и повреждения от взрыва можно указать либо через проперти explosiondamage, explosionradius, либо в скобках после самой команды; если в третьей позиции (donthurtshooter) стоит 1 – взрыв никогда не поранит самого стрелка, как бы близко к взорвавшемуся проджектайлу он ни стоял (по умолчанию, естественно, 0).

A_Recoil(#) – отталкивает актора на # единиц назад, обычно используется для симуляции отдачи оружия (однако можно использовать с монстрами и давать команде отрицательные значения, с помощью чего, например, можно создать эффект резкого рывка монстра к игроку, примерно как у  Lost Soul)

A_Jump(chance,offset) – с шансом chance (0-256) анимация актора в пределах данного стейта перескочит на offset (цифра) кадров, либо можно указать в кавычках название стейта, к которому идет прыжок (например A_Jump(200,"death") – с шансом 200 из 256 перескочит на стейт анимации смерти).

A_JumpIfInventory ("itemname", itemamount, offset) – при наличии в инвентаре предмета itemname в количестве НЕ МЕНЕЕ itemamount (цифра) анимация перескочит в пределах стейта на offset (цифра) кадров вперед или прямо в стейт (тогда его название надо указать в кавычках).

A_PlaySound("soundname")актор издаст звук soundname.

A_PlaySoundEx("soundname","sounchannel",loop,attenuation)аналогично A_PlaySound, но более широкий по возможностям, например можно указать канал soundchannel. Звуки, играющие на разных каналах, могут звучать параллельно, не прерывая друг друга. Возможные каналы: Auto, Weapon, Voice, Item, Body, SoundSlot5, SoundSlot6, SoundSlot7. Если loop = 1, то звук будет повторяться (по умолчанию 0, обычно это не нужно, его потом можно прервать командой A_StopSoundEx("soundchannel")). Attenuation регулирует pfnb[fybt звука на расстоянии: -1 – становится тише уже на близком расстоянии, 0 – как обычно, 1 – слышен на всем уровне, 2 – слышен на всем уровне и отовсюду (нельзя уловить источник звука на слух).

A_SpawnItem ("itemname",distance,Zoffset,useammo,translation) – создать перед актором предмет itemname на расстоянии distance (если не указано, то автоматически выбирается минимальное расстояние, чтобы итем не попал внутрь актора), смещается по высоте на Zoffset (цифра). Дальше сложнее. Если актор – оружие, то оно за спаун предмета либо тратит патроны, либо нет (четвертая позиция – useammo, либо 0, либо 1), если актор – монстр, то при 1 в четвертой позиции появятся отношения "мастер-миньон", при наличии которых можно использовать у появившихся итемов команды A_KillMaster (убить создателя) или у актора-создателя A_KillChildren (убить созданных). Последняя позиция – цветовая трансляция (подробнее обсудим в разделе про монстров).

 

Команды, управляющие оружием игрока мы рассмотрим в разделе про оружие.

 

Также существует маленький набор команд из скриптового языка ACS, который применяется в (Г)ЗДуме, которые можно применять в Декорейте. Они имеют отличный от других команд вид (нет "A_"):

 

ACS_Execute(#) – выполнить скрипт номер #

healthing(#) – вылечить актора на # хелсов

damagething(#) – отнять у актора # хелсов

 

Существуют и другие скриптовые команды, которые можно использовать в Декорейте. Не забудьте заглянуть в статью Action Functions.

 

 

Классы акторов

<<СОДЕРЖАНИЕ>>         

 

Строго говоря, есть один базовый класс предметов: Inventory. Также есть класс самого игрока PlayerPawn. Все остальные объекты являются подклассами Inventory. Исключение составляют монстры и проджектайлы (снаряды) – они вообще не имеют отдельного класса и обозначаются как монстры/проджектайлы через проперти monster и projectile соответственно.

В целом, если считать подклассы Inventory, классов акторов великое множество. Из них часть нам не нужны совсем, а без некоторых обойтись нельзя. Вот последние мы и рассмотрим.

 

PlayerPawn – это класс, обозначающий игрока

Inventory – использование Inventory как такового обозначает обычные предметы инвентаря, которые могут использоваться самыми разными способами

CustomInventory – особый вид предметов инвентаря, позволяющих использование некоторых дополнительных команд

Health – это любые лечащие предметы инвентаря

HealthPickup – это лечащие предметы, которые лечат не сразу, а идут в инвентарь и лечат, когда используются

PowerupGiver – предмет с эффектом какого-либо пауэр-апа (невидимость, неуязвимость и т.п.), варианты пауэр-апов лимитированы, но их можно сочетать

Ammo – как нетрудно догадаться, это патроны для Weapon

Weapon – это оружие игрока

WeaponPiece – это часть оружия, может использоваться для создания оружия, которое надо собирать (как последнее оружие в Hexen, например)

 

 

PlayerPawn

<<СОДЕРЖАНИЕ>>         

 

Это уникальный класс акторов – игрок. Он обозначает свойства самого игрока (или доступных игроку классов, которых может быть и несколько, как в Hexen). Во многом он похож на монстра.

Возможные стейты этого актора: Spawn, See, Missile, Melee, Pain, Death, Xdeath (в классическом Думе) и AltSkindDeath, AltSkinXDeath (в (Г)ЗДуме).

Мы разберем этот класс акторов на примере стандартного думовского игрока:

 

DECORATE

ACTOR DoomPlayer : PlayerPawn

{

      Speed 1

      Health 100

      Radius 16

      Height 56

      Mass 100

      PainChance 255 //у игрока практически всегда проигрывается анимация боли, когда по нему попадают

      Player.ColorRange 112, 127 //эта проперти управляет областью цвета игрока, которую можно заменить траснялцией (подробнее позже)

      Player.DisplayName "Marine"

      Player.CrouchSprite "PLYC" //это введенный в новых версиях (Г)ЗДума отдельный спрайт для присевшего игрока

      Player.StartItem "Pistol" //у игрока с начала уровня всегда есть пистолет

      Player.StartItem "Fist" //и кулак у него тоже есть

      Player.StartItem "Clip", 50 //а также 50 патронов к пистолету

            States

      {

      Spawn:

            PLAY A -1

            Loop

      See: //анимация движения актора

            PLAY ABCD 4

            Loop

      Missile: //этот стейт управляет анимацией атаки из оружия дальнего боя, он есть и у монстров (у которых есть дистанционная атака)

            PLAY E 12

            Goto Spawn

      Melee: //этот стейт управляет анимацией атаки из оружия ближнего боя, он есть и у монстров (у которых есть ближняя атака)

            PLAY F 6 BRIGHT

            Goto Missile

      Pain: //стейт боли, активируется, когда в актора попадают (но у игрока при этом не сбивается атака, в отличие от монстра)

            PLAY G 4

            PLAY G 4 A_Pain //играет звук боли актора

            Goto Spawn

      Death: //анимация смерти

            PLAY H 10 A_PlayerSkinCheck("AltSkinDeath") //эта команда проверяет, существуют ли новые спрайты смерти, и, если да, запускает стейт AltSkinDeath

            PLAY I 10 A_PlayerScream //эта команда озвучивает заранее созданный звук смерти игрока

            PLAY J 10 A_NoBlocking //эта команда вам знакома, актор становится неплотным и роняет свои предметы (если они прописаны в dropitem)

            PLAY KLM 10

            PLAY N -1

            Stop

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

            PLAY O 5 A_PlayerSkinCheck("AltSkinXDeath") //эта команда проверяет, существуют ли новые спрайты смерти при разрывании в мясо, и, если да, запускает стейт AltSkinXDeath

            PLAY P 5 A_Xscream //играет звук "misc/gibbed" – вопль игрока при разрывании в мясо

            PLAY Q 5 A_NoBlocking

            PLAY RSTUV 5

            PLAY W -1

            Stop

      AltSkinDeath: //альтернативная анимация смерти

            PLAY H 6

            PLAY I 6 A_PlayerScream

            PLAY JK 6

            PLAY L 6 A_NoBlocking

            PLAY MNO 6

            PLAY P -1

            Stop

      AltSkinXDeath: //альтернативная анимация смерти с разрыванием в мясо

            PLAY Q 5 A_PlayerScream

            PLAY R 0 A_NoBlocking

            PLAY R 5 A_SkullPop //в скобках надо указать имя актора, тогда этот актор вылетит из умирающего игрока как снаряд (предположительно, это должен быть какой-нибудь череп, но вы можете сделать что угодно)

            PLAY STUVWX 5

            PLAY Y -1

            Stop

      }

}

 

В принципе, никаких особых дополнительных комментариев тут не нужно. Стоит только заметить, что вы можете замедлить игрока, например, поставив ему скорость 0.8. Также здесь не написаны несколько возможных для использования проперти:

 

player.viewheight # - точка расположения глаз игрока будет # (можно сделать его выше)

player.attackzoffset # - это изменение высоты точки, из которой игрок выпускает пули и снаряды, это необходимо, если вы меняете его рост

player.maxhealth # - игрок не может набрать более # хелсов никакими лечилками

player.runhealth # - если у игрока хелсов меньше #, он не может бегать и переходит на шаг

player.jumpz # - высота прыжка игрока в (Г)ЗДуме равна #

player.damagescreencolor colorname – цвет, в который окрашивается экран, когда по игроку попадают

player.invulnerabilitymode mode – тип неуязвимости: либо нет, либо Ghost, либо Reflective (для подробностей – пункт про пауэр-апы)

 

Подробнее отметим player.displayname. После этой строчки указывается имя класса игрока. Вы можете создать несколько классов, и тогда в начале новой игры сможете выбрать один из них – в меню выбора игрока будет указываться имя, указанное в player.displayname. Имейте в виду, что вам надо не забыть добавить созданные классы в KEYCONF. Но в KEYCONF вы добавляете классы через имя актора, обозначающего игрока, а не через его player.displayname.

 

Есть и другие проперти, например, позволяющие управлять отдельно скоростью игрока при движении в разные стороны. Все они есть в PlayerPawn.

 

 

Inventory

<<СОДЕРЖАНИЕ>>         

 

Описание Inventory в ZDoom Wiki.

 

Этот класс широк и может обозначать почти что угодно и даже может обозначать ничто (иногда нам понадобятся фальшивые предметы, но об этом позже). Однако Inventory напрямую почти не используется, потому что обычно предметы, которые мы подбираем, не являются просто предметами, а что-либо нам дают – а поэтому они уже обычно относятся либо к Ammo, либо к Health, либо к чему-либо еще. Стейт, воспринимаемый этим классом, всего один – Spawn – но существуют разнообразные проперти и флаги, которые надо знать.

 

DECORATE

actor Something : Inventory

{

inventory.amount 1 //сколько дается единиц этого предмета, когда мы его подбираем

inventory.maxamount 10 //сколько максимум может быть у нас единиц этого предмета

inventory.icon "SOMETHNG" //спрайт для иконки этого предмета в инвентаре

inventory.pickupmessage "You picked up some item!" //сообщение, возникающее, когда мы подбираем предмет

inventory.pickupsound "pickups/something" //звук из SNDINFO, который звучит, когда мы берем предмет

+INVBAR //наличие этого предмета отображается в инвентаре (используется вышеуказанная иконка)

-ALWAYSPICKUP //мы НЕ МОЖЕМ взять этот предмет, если у нас уже максимум

states

{

Spawn: //управляет поведением актора при его появлении

SMTH A 1

loop

}

 

 

Итак, тут у нас актор Something. Следует дать некоторые комментарии. Для начала стоит помнить, что, даже если некоторые проперти и флаги не указаны, они все равно есть – просто ставятся значения по умолчанию, которые существуют для каждого флага и каждой проперти.

inventory.amount можно не ставить, если оно равно 1, потому что по умолчанию оно равно 1

Если не указать inventory.icon у предмета, идущего в инвентарь, то его инвентарным спрайтом автоматически станет первый спрайт из спауна, в данном случае SMTHA0.

По умолчанию INVBAR выключен (-INVBAR), и предметы в инвентаре не отображаются.

По умолчанию у предметов кроме аптечек и оружия/патронов стоит +ALWAYSPICKUP, поэтому в Думе мы можем подобрать сколько угодно Соулсфер, даже если хелсов уже 200, но не можем брать больше аптечек, чем можно (но это ограничения вводятся не флагами, а особыми проперти Health итемов, об этом ниже).

 

Возможности чистого Inventory достаточно низки, но именно он применяется в случаях, когда используются команды A_JumpIfInventory.

 

Для более сложных случаев есть...

 

 

CustomInventory

<<СОДЕРЖАНИЕ>>         

 

Описание CustomInventory в ZDoom Wiki.

 

Данный тип акторов имеет стейты обычного Inventory. Отличие этого класса от обычного Inventory в том, что к нему можно привязывать команды Декорейта, а также некоторые скриптовые команды, которые можно использовать в Декорейте, кроме того у него есть дополнительные стейты. Например команду healthing(#) (вылечить актора-активатора команды на # хелсов).

Приведу пример на новом варианте того же Something:

 

DECORATE

actor Something : CustomInventory

{

inventory.amount 1

inventory.maxamount 10

inventory.icon "SOMETHNG"

inventory.pickupmessage "You picked up some item!"

inventory.pickupsound "pickups/something"

-AUTOACTIVATE

+INVBAR

-ALWAYSPICKUP

states

{

Spawn:

     SMTH A 1

     loop

Pickup: //управляет поведением предмета во время его взятия

     TNT1 A 0

     TNT1 A 0 healthing(200)

     stop

Use: //управляет поведением предмета во время использования

     TNT1 A 0

     TNT1 A 0 A_GiveInventory("InvulnerabilitySphere",1)

     stop

}

 

 

Когда игрок берет этот итем, игрок лечится на 200 хелсов. А когда использует, ему дается Неуязвимость (обычная, думовская, которая уже есть в игре). Если бы мы привязали эти команды к обычному Inventory, они бы не стали работать. Таким образом можно создать предмет, который будет сразу давать кучу вещей и эффектов.

Однако к этому классу нельзя воззвать через A_JumpIfInventory.

 

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

Если у предмета стоит флаг –AUTOACTIVATE, он не будет сразу использоваться, а при взятии попадет в инвентарь, откуда его можно будет использовать соответствующей кнопкой (как в Хексене или Дюк Нюкеме), если же AUTOACTIVATE включен – стейт use активируется автоматически.

 

Вы заметили, что в стейты Pickup и Use команды привязаны к пустым стейтам. Это естественно, т.к. эти команды выполняются, когда предмет уже взят, т.е. никакого внешнего вида он не имеет. Однако вы также заметили, что я зачем-то вставил пустые кадры без команд в начала стейтов. Зачем? Дело в том, что, если привязывать команды к кадрам нулевой длины, то, когда вы вставляете их в самое начало стейта, это обычно вызывает глюки, в результате чего в игре привязанные к кадрам команды то выполняются, то нет. Выхода два: либо дать стейту любую длительность, либо поставить перед ним еще один пустой кадр, что я и сделал. На самом деле, учитывая что, как я уже говорил, все эти команды выполняются разом, можно и указать время – это уже дело личной привычки.

 

Помните, что вы НЕ МОЖЕТЕ использовать в команде A_JumpIfInventory предмет класса CustomInventory!

 

 

Health

<<СОДЕРЖАНИЕ>>         

 

Описание Health в ZDoom Wiki.

 

Данный тип итемов обозначет любой вид лечащих итемов. Они мало чем отличаются от обычного CustomInventory. В данном случае inventory.amount и inventory.maxamount управляют числом хелсов, получаемых при взятии предмета и максимально возможном для получения данным предметом.

В дополнение используется проперти health.lowmessage # "messagetext", с помощью которой при взятии этого предмета, если у игрока меньше # хелсов вместо обычного inventory.pickupmessage появляется messagetext.

Например:

 

DECORATE

actor Whiskey : Health

{        

  inventory.pickupmessage "You drank some booze."

  inventory.pickupsound "pickups/whiskey"

  health.lowmessage 25 "Booze really picked you up!"

  inventory.amount 20

  inventory.maxamount 100

  +COUNTITEM

  states

  {

  Spawn:

    RWHI A -1

    stop

  }

}

 

Надо, однако, сделать небольшое замечание. В большинстве случаев в игре есть два вида основных аптечек – маленькая и большая. Если вы решите сделать новые аптечки, вам придется заменить их обе, пользуясь описанной выше системой частичного копирования параметров актора. Это выглядит следующим образом:

 

DECORATE

actor Whiskey : Health replaces Stimpack //бутылка виски замещает обычную маленькую думовскую аптечку

{        

  inventory.pickupmessage "You drank some booze."

  inventory.pickupsound "pickups/whiskey"

  health.lowmessage 25 "Booze really picked you up!"

  inventory.amount 20

  inventory.maxamount 100

  +COUNTITEM

  states

  {

  Spawn:

    RWHI A -1

    stop

  }

}

 

actor BigWhiskey : Whiskey replaces Medikit //большая бутылка виски, перенимая все от маленькой, заменяет обычную большую думовскую аптечку

{

inventory.amount 50 //однако этот актор отличается от предыдущего тем, что дает 50 хелсов, а не 20

inventory.pickupsound "pickups/bigwhiskey" //и звук при подбирании его другой

scale 2 //а также визуально он больше в 2 раза, хотя спрайт тот же, потому что мы не трогали стейты

}

 

 

Стоит также добавить, что мощные лечащие средства (думовская Мегасфера, например) не являются Health, они относятся к пауэр-апам, поэтому о них подробнее ниже.

Также флаг AUTOACTIVATE не оказывает никакого эффекта на итемы этого класса – они всегда берутся и лечат вас автоматически. Для других случаев есть...

 

 

HealthPickup

<<СОДЕРЖАНИЕ>>         

 

Описание HealthPickup в ZDoom Wiki.

 

Это почти обычные лечащие предметы, только они идут в инвентарь и лечат при использовании (как, например, Quartz Flask в Hexen). У них автоматически активирован флаг +INVBAR. Все, что нам понадобится, это в дополнение к обычным параметрам Health итемов указать inventory.icon. Если мы его не укажем, инвентарной иконкой станет первый кадр в стейте Spawn.

 

DECORATE

actor Whiskey : HealthPickup replaces Stimpack

{        

  inventory.pickupmessage "You picked up a bottle of whiskey."

  inventory.icon "WHISKEY"

  inventory.amount 20

  inventory.maxamount 100

  +COUNTITEM

  states

  {

  Spawn:

    RWHI A -1

    stop

  }

}

 

 

 

PowerupGiver

<<СОДЕРЖАНИЕ>>         

 

Описание PoweupGiver в ZDoom Wiki.

 

Итемы этого класса дают игроку разнообразные эффекты пауэр-апов. Его возможные стейты – все те же самые, что у Inventory: Spawn, Pickup, Use. У кого-то мог уже возникнуть интерес, почему класс называется PowerupGiver, а не просто PowerUp. Действительно, Powerup'ы существуют, но итемы этого класса жестко записаны в движок лаунчера, и их НЕЛЬЗЯ использовать напрямую, они используются только через PowerupGiver.

Шаблон PowerupGiver выглядит следующим образом:

 

DECORATE

actor Itemname : PowerupGiver

{        

  inventory.pickupmessage "Message"

  inventory.pickupsound "sound"

  powerup.color colorname # //цвет, который приобретет экран при взятии итема, colorname – имя цвета, # – уровень прозрачности экрана (цифра)

//powerup.color palettename //это другой вариант указывания цвета, используя одну из палитр: GoldMap или InverseMap

  powerup.type "poweruptypename" //а вот здесь идет отсылка к самому пауэр-апу, одному из существующих в лаунчере; можно писать без кавычек

  powerup.duration # //длительность действия пауэр-апа в тиках

  powerup.mode "modename" //режим пауэр-апа, на данный момент указывает только два возможных вида неуязвимости

  +COUNTITEM //для пауэр-апов типично, что собирание их учитывается в процентной статистике по собранным предметам в конце уровня

  states

  {

  Spawn:

    RWHI A -1

    stop

  }

}

 

 

Теперь поподробнее. Цвет – это накладываемый на экран во время действия пауэр-апа цветной слой. Разумеется, если его прозрачность будет равна 1.0, мы ничего не увидим, т.к. цвет полностью перекроет экран. Поэтому не рекомендуется ставить больше 0.25. Другой вариант, вместо названия цвета и числа, обозначающего прозрачность, мы можем выбрать одну из двух палитр – GoldMap или InverseMap. Каждая из них не накладывает на экран цвет, а изменяет саму палитру, которая используется в игре для отображения цвета текстур, спрайтов и любой другой графики. GoldMap – это палитра неуязвимости из Heretic, вся выполнена в золотых оттенках. InverseMap – это знакомая вам черно-бело-негативная палитра думовской неуязвимости.

powerup.mode подходит только для пауэр-апа типа Неуязвимость (Invulnerable), и режим может быть "none" (или просто не писать эту строчку) – тогда это просто обычная неуязвимость ­– может быть "reflective" – в этом случае во время действия пауэр-апа от игрока еще и будут отражаться летящие в него проджектайлы (и они могут повредить и тех, кто их запускал) или "ghost" – судя по всему, в этом случае в состоянии неуязвимости игрок будет быстро то и дело приобретать, а потом терять и снова приобретать эффект пауэр-апа Ghost. Заметьте, что вид неуязвимости можно указывать не только в коде пауэр-апа, но и в коде самого игрока (player.invulnerabilitymode modename управляет этой функцией).

Замечу, что в (Г)ЗДуме итем Infrared (очки ночного видения) тоже использует какую-то зеленоватую палитру, но мне не известен способ ее использовать. Касательно ее отключения и возврата очков ночного видения к виду классики – в последних версиях (G)ZDoom появилась соответствующая строчка в опциях..

Теперь к самому интересному – типы пауэр-апов. Они указываются командой powerup.type. Их число и свойства фиксированы, однако не отчаивайтесь – во-первых, их много, во-вторых, лаунчеры обновляются, и появляются новые типы пауэр-апов. Итак, вот они:

 

Invulnerable – бессмертие

Strength  дает эффект думовского берсерка (красный экран, усиление удара кулаком, легкое ослабение удара и эффект до конца уровня)

Invisibility – актор полупрозрачен, видевшие его монстры плохо по нему попадают, не видевшие не видят его, пока он их не заденет или не подойдет слишком близко

Ghost или Shadow – актор полупрозрачен, видевшие его монстры плохо по нему попадают, не видевшие не видят его, пока он их не заденет, также актора не может поранить проджектайл с флагом +THRUGHOST

IronFeet – эффект думовского антирадиационного костюма, он защищает почти от всех лавоподобных повреждающих эффектов (за исключением некоторых, очень немногих)

LightAmp или Torch – освещение всего уровня, эффект думовских очков ночного видения (или Hexen'овского факела)

Flight – возможность летать (как при консольной команде fly)

WeaponLevel2 – оружие актора переключается на улучшенный вариант, эффект Tome of Power (подробнее в разделе про оружие)

Speed – ускорение движения; с определенной версии порта GZDoom этот пауэр-ап перестал работать, но в данный момент снова работает как надо

Frightener – монстры убегают от игрока, вместо того, чтобы атаковать его

Damage – увеличение повреждений всего оружия (это не то же самое, что Strength)

Drain – каждый раз, нанося повреждения монстрам, игрок восстанавливает себе аналогичное количество хелсов (своеобразный вампиризм)

TimeFreezer – заморозка всех акторов на уровне, кроме игрока (как при консольной команде freeze)

Regeneration – каждые 3 секунды восстанавливается 5 хелсов (как регенерация в Quake III)

Protection – часть получаемых игроком повреждений поглощается

HighJump – увеличенная в разы высота прыжка

 

Определенно, у вас остались некоторые вопросы. Поэтому дам некоторые пояснения. То, что я описал выше – это ТИПЫ ПАУЭР-АПОВ. Они указываются в строчке powerup.type "poweruptypename".

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

 

PowerDamage – управляет пауэр-апом типа "Damage"

PowerProtecionуправляет пауэр-апом типа "Protection"

 

Почему я упоминаю их отдельно? Да потому, что, как вы могли заметить, у PowerupGiver не так уж много дополнительных проперти. А как же мы тогда укажем, во сколько раз будут возрастать повреждения (Damage) или на сколько нас будет защищать пауэр-ап (Protection)?

Вот для этого нам и понадобится делать отсылки к вышеуказанным итемам. Но их НЕЛЬЗЯ использовать напрямую. В общем, в этом случае нам придется создавать новый пауэр-ап в два шага путем знакомого нам частичного заимствования свойств:

 

DECORATE

actor PowerQuadDamage : PowerDamage //тут мы делаем отсылку к PowerDamage

{

damagefactor "normal", 4 //а данная проперти указывает, что повреждения будут возрастать в 4 раза и они будут "нормального" типа

}

 

actor QuadDamage : PowerupGiver

{

 inventory.pickupmessage "Quad Damage!!"

 powerup.color LawnGreen 0.25

inventory.usesound "pickups/quaddamage"

 powerup.type QuadDamage //заметьте, а вот тут мы уже ссылаемся уже на созданный лично нами пауэр-ап, а не на существующий в лаунчере

 powerup.duration 1000

 +AUTOACTIVATE

 +FANCYPICKUPSOUND //это флаг класса Inventory, с ним звук поднятия предмета слышен на всем уровне всем игрокам и монстрам

 states

 {

 Spawn:

   MEGA ABCD 4 bright

   loop

 }

}

 

 

Вот таким вот образом мы создали аналог квейковского Quad Damage – вначале мы сделали пауэр-ап нового типа, а затем сделали PowerupGiver, отсылающийся к созданному нами пауэр-апу.

О виде повреждений (в данном случае "normal") мы поговорим в разделе про оружие.

 

В Protection все выглядит абсолютно точно также, только там в damagefactor "damagefactortype", #, где число # будет указывать, во сколько раз уменьшаются получаемые игроком повреждения (например, 0.5 – уменьшение вдвое). При желании можно сделать наоборот, антибонус, который будет увеличивать уязвимость игрока. А можно и сделать пауэр-ап неуязвимости через Protection. Ну и, конечно, там будет частичное заимствование параметров PowerProtection.

 

Думаете, это все? Отнюдь нет. Во-первых, не забывайте, что вы можете сочетать PowerupGiver со стандартными итемовскими флагами, например -ALWAYSPICKUP, +INVBAR, -AUTOACTIVATE дадут вам сделать пауэр-ап, который можно поместить в инвентарь и использовать по желанию.

 

К тому же, можно создавать комплексные пауэр-апы через CustomInventory. Например так:

 

DECORATE

actor PowerQuadDamage : PowerDamage

{

damagefactor "normal", 4

}

 

Actor QuadDamage : PowerupGiver

{

poweruptype QuadDamage

powerup.duration 1000

+AUTOACTIVATE

}

 

actor PowerHalfDamage : PowerProtection

{

damagefactor "normal", 0.1

}

 

Actor HalfDamage : PowerupGiver

{

poweruptype HalfDamage

powerup.duration 1000

+AUTOACTIVATE

}

 

 

actor Invisibility : InvisibilitySphere //InvisibilitySphere – это стандартная думовская невидимость

{

powerup.color Green 0.2

powerup.duration 1000

+AUTOACTIVATE

}

 

 

actor SuperDrug : CustomInventory //а здесь мы используем CustomInventory

{

 inventory.pickupmessage "You made drug injection!!"

 inventory.usesound "pickups/drug"

 +FANCYPICKUPSOUND //звук взятия слышен на всем уровне

 states

 {

 Spawn:

   MEGA ABCD 4 bright

   loop

 Pickup: //и добавляем стейт пикапа

   TNT1 A 0

   TNT1 A 0 A_giveinventory("QuadDamage",1) //при поднятии итем дает нам вышеуказанный усилитель атаки

   TNT1 A 0 A_giveinventory("HalfDamage",1) //а также уменьшитель повреждений

   TNT1 A 0 A_giveinventory("Invisibility",1) //а также невидимость

   stop

 }

}

 

 

 

Здесь мы сразу получаем три пауэр-апа при взятии одного. При этим снова заметьте, что сами пауэр-апы напрямую использовать нельзя, а этот SuperDrug дает нам созданные нами PowerupGiver'ы.

Таких сочетаний может быть множество. Только обратите внимание, что в данном случае в игре в качестве пауэр-апа на самом деле будет выступать CustomInventory, который эти пауэр-апы дает, а в этой связи все, что касается проперти самого пауэр-апа (в данном случае, длительность и цвет), должно быть прописано в свойствах PowerupGiver'ов, которые нам дает этот итем.

 

 

Ammo

<<СОДЕРЖАНИЕ>>         

 

Это простой тип инвентаря, в котором самое важное – цифры. Разберем его на простом примере:

 

DECORATE

actor Clip : Ammo 2007 //стандартные думовские патроны для пистолета и пулемета

{

  spawnid 11

  radius 20 //радиус лежащих патронов

  height 16 //высота лежащих патронов

  inventory.pickupmessage "Picked up a clip." //сообщение при поднятии патронов

  inventory.icon "CLIPA0" //так патроны отмечаются в HUD

  inventory.amount 10 //столько мы подбираем патронов за раз

  inventory.maxamount 200 //но не можем взять больше этого числа

  ammo.backpackamount 10 //а столько патронов мы получаем, когда берем пауэр-ап Ammo Pack

  ammo.backpackmaxamount 400 //и таков становится максимум патронов после взятия Ammo Pack

  states

  {

  Spawn:

    CLIP A -1

    stop

  }

}

 

Никаких особых дополнительных комментариев не нужно. Ammo использует единственный стейт Spawn.

Единственное, что стоит отметить:

inventory.pickupsound "soundname" – звук поднятия предмета можно привязать и к патронам, в обычном Думе звук одинаков для всех, но мы можем привязать отдельные

+IGNORESKILL – этот флаг я уже упоминал, если мы его включим, то количество патронов всегда будет одинаково, независимо от уровня сложности (по умолчанию на "I'm Too Young To Die" количество патронов удваивается)

 

 

Проджектайлы

<<СОДЕРЖАНИЕ>>         

 

По плану мы должны были бы начать разбирать на данном этапе Weapon, но без знания особенностей проджектайлов это может вызвать некоторые затруднения. Поэтому придется начать с них.

Актор становится проджектайлом, если у него в проперти стоит проперти projectile. Никаких параметров у этой проперти нет. Не путайте, это не флаг, это именно проперти, поэтому никаких плюсов-минусов. Когда вы выставляете эту проперти, к акторму автоматически привязываются следующие флаги:

+NOBLOCKMAP

+NOGRAVITY

+DROPOFF

+MISSILE

+ACTIVATEIMPACT

+ACTIVATEPCROSS

+NOTELEPORT

 

Основные проперти проджектайла следующие:

 

speed

damage

radius

height

 

Их мы рассматривали в разделе "Проперти", они нам знакомы. Но некоторые нужно отметить:

 

seesound "soundname" – в случае с проджектайлом этим звуком будет звук, с котором проджектайл появляется (звук можно либо привязать к проперти проджектайла, либо привязать к анимации стрельбы оружия, я предпочитаю второй вариант)

deathsound "soundname" – а это звук при смерти (уничтожении, взрыве) проджектайла, и его никуда кроме проджектайла не привяжешь

 

Никаких других звуков у проджектайла нет – очевидно, это ведь не монстр, у него нет реакций. Хотя это не значит что их нельзя сделать. Достаточно вписать в него какой-нибудь A_Playsound в стейт Spawn, чтобы проджектайл в полете издавал какой-нибудь звук – например, гудение или что-нибудь в этом роде.

 

Есть также специальная команда A_Explode(explosiondamage,explosionradius), где первый пункт – повреждения взрыва (не путайте их с повреждениями самой ракеты, это два разных параметра), а второй – радиус взрыва. Если их не указать, то они примут значения по умолчанию, равные таковым у стандартной думовской ракеты. Какие они именно – я не знаю.

 

Разберем простой проджектайл на примере обычной думовской ракеты:

 

DECORATE

actor Rocket

{

  spawnid 127

  obituary "%o rode %k's rocket."

  radius 11 //радиус ракеты (несмотря на внешний вид, логически в игре ракета круглая)

  height 8 //высота ракеты

  speed 20 //скорость полета

  damage 20 //врезавшись, ракета наносит 20 повреждений

  seesound "weapons/rocklf" //звук выстрела ракетницы

  deathsound "weapons/rocklx" //звук взрыва ракеты

  PROJECTILE //а вот и нужная проперти

  +RANDOMIZE //этот флаг включает случайное изменение длины первого кадра анимации Spawn стейта проджектайла (по-моему, бесполезная вещь)

  +ROCKETTRAIL //знакомый нам флаг, привязывает к ракете (Г)ЗДумовский ракетный след

  states

  {

  Spawn:

    MISL A 1 bright

    loop

  Death:

    MISL B 8 bright A_Explode //а вот и команда, активирующая взрыв

    MISL C 6 bright

    MISL D 4 bright

    stop

  }

}

 

Между прочим, можно было привязать A_Explode к кадру нулевой длины перед самой анимацией взрыва – разницы не было бы.

 

Мы можем немного изменить ракету по своему желанию:

 

DECORATE

actor NewRocket : Rocket replaces Rocket //мы заменяем старую ракету новой, которая забирает от старой почти все свойства

{

  damage 30 //но у нее поврежденяи 30

  +RIPPER //и она летит сквозь монстров, не взрываясь

  states //заметьте, нам не надо заново писать все стейты при заимствовании, а только те, которые мы изменяем

  {

  Death: //мы изменяем только стейт Death, убрав из него A_Explode

    MISL B 8 bright

    MISL C 6 bright

    MISL D 4 bright

    stop

  }

}

 

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

Заметьте, что мы не вносим никаких изменений в оружие, мы просто заменяем одну ракету другой, поэтому, когда оружие должно выпустить Rocket, оно будет выпускать NewRocket.

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

 

Теперь поговорим о типах повреждений – damagetype. Проджектайл может иметь проперти, регулирующую его, так сказать, "элемент" (почти как в ролевых играх). Например, вспомните магическую заморозку в Hexen (или немагическую заморозку в Duke Nukem 3D) – это можно осуществить и в Думе.

ПРИМЕЧАНИЕ: у проджектайла можно указать и более одного damagetype, и игра запустится корректно, однако работать будет только один из указанных типов демеджа (по моим наблюдениям – тот, что был указан последним).

Изначально существовал флаг +ICEDAMAGE, дававший проджектайлу соответствующий эффект, но теперь это осталось в прошлом, и мы полностью ориентируемся на damagetype проперти.

Собственно, что оно делает? Для начала мы разберем damagetype ice, потому что этот тип уже вписан в движок, и нам ничего особенного добавлять не нужно.

 

Если мы припишем проджектайлу этот тип, например...

 

DECORATE

actor FreezingRocket : Rocket replaces Rocket

{

damagetype ice

}

 

...то, когда этот проджектайл будет убивать врага, враг не будет показывать свою обычную анимацию из Death стейта, а вместо этого застынет и станет голубоватым, а потом разлетится на кусочки – как в Hexen после заморозки. В данном случае это не дает ничего, кроме визуального эффекта. Ничего дополнительного для создания такого эффекта нам делать не надо – просто прописать этот тип демеджа у нужного проджектайла.

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

 

DECORATE

actor FlamingRocket : Rocket replaces Rocket

{

damagetype fire

}

 

Этого мало, нам придется модифицировать всех врагов, которые будут воспринимать эту ракету как особую. Например:

 

DECORATE

actor DoomImp1 : DoomImp replaces DoomImp //заменяем импа

{

states

{

pain.fire:

... //здесь у нас новая анимация боли, которая будет работать при попадании в этого монстра соответствующим снарядом

death.fire:

... //здесь у нас новая анимация смерти

}

 

То есть, при создании нового типа демеджа мы можем создать отдельные стейты Pain и Death привязанные к этому типу демеджа. Pain можем не создавать, но Death нам понадобится. Например, мы можем создать огненные снаряды, при смерти от которых у акторов будет новая анимация со спрайтами сгорающей тушки. Это применялось в игре Blood. Если мы не создадим врагам специальных стейтов pain.damagetypename и death.damagetypename, эти стейты не будут активироваться, и вместо них будут работать обычные.

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

Однако, надо отметить, что ice и fire стоят особняком, потому что существуют специальные флаги:

 

+FIRERESIST – уменьшение вчетверо повреждения от fire

+NOICEDEATH – у актора не включается анимация замороженной смерти, реагирует на ice снаряды как на обычные

 

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

 

damagefactor type, # - где type – название соответствующего демеджтайпа, а # - уровень восприимчивости актора к нему (от 0 до 256). Не путайте это с одноименной проперти, управляющей пауэр-апом Protection.

Простой пример:

 

DECORATE

Actor DarkImp : Imp replaces Imp

{

...

damagefactor "normal",0.3 //это стандартный тип демеджа всех проджектайлов, стоит по умолчанию, в данном случае малоэффективен

damagefactor "magic",2 //а проджектайлы этого типа будут бить монстра в 2 раза мощнее

damagefactor "darkmagic",0 //а этого типа – вообще не будут

...

}

 

 

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

 

Иногда, при создании новых типов проджектайлов, нам может понадобиться воспользоваться созданием своих типов демеджа и восприимчивости к ним, чтобы избежать такую ситуацию, когда монстр сам себя ранит своим снарядом. Конечно, чтобы монстры не били друг друга, сущесвует флаг +DONTHURTSPECIES, однако, что делать, если монстр выпускает проджектайл, который спаунит вторичные проджектайлы? Вот тогда надо всем этим снарядом сделать свой тип демеджа и поставить демеджфактор у монстра от этого типа демеджа на 0.

 

Способов применения этой технологии много – в том числе можно развить в Думе подобие РПГ-системы с элементальным оружием и врагами. Можно просто создать множество оружия, оказывающего на врагов разный эффект – это я по максимуму использовал в своем моде "Wizardry 2".

Но, помимо всего этого, я нашел один нестандартный способ применения этих фунций: с их помощью можно создать разбиваемую ракетой стену, как в Duke Nukem 3D и Shadow Warrior.

 Дело в том, что в Думе в редакторах карт штатными средствами нельзя создать стену, которая будет разбиваться только ракетой (или каким-то другим определенным проджектайлом) и не будет разбиваться ничем еще. А я нашел такой способ. Делаем так:

 

DECORATE

Actor Cracker : Rocket replaces Rocket

{

damagetype blow //я присваиваю измененной ракете особый тип демеджа, в остальном она остается обычной ракетой

}

 

Actor Crack 30003 //с помощью этого актора и будет создаваться эффект трещины; нам понадобится дать ему id, иначе в редакторе карт мы не сможем вызвать {                   //этот объект

spawnid 203 //нам понадобится дать ему spawn number

speed 0

MONSTER //актор должен быть монстром, иначе мы не сможем приписать ему стейты Pain и Death

+FRIENDLY //он должен быть дружелюбным, или мы должны поставить флаг –COUNTKILL, чтобы его уничтожение не считалось в статистике по убитым врагам

height 128

radius 32

health 9999 //большое количество хелсов не позволит уничтожить актора обычным оружием

painchance 256 //256 – самый большой пейншанс, при попадании в актора любым оружием он мгновенно будет попадать в стейт Pain

+noblood //поскольку по сути это не монстр, у него не должно быть крови

states

{

spawn:

TNT1 A 1 //в игре этот объект не будет видим, поэтому пустой спрайт

loop

pain.blow:

TNT1 A 0

goto death.blow //попав в стейт Pain, актор мгновенно отправляется к стейту смерти

death.blow:

TNT1 A 0

TNT1 A 0 Acs_execute(4) //а эта команда запускает в игре скрипт № 4

TNT1 A 0 A_playsound("Blowtrash") //и проигрывается звук обрушающейся стены

stop

}

}

 

 

Обычные враги будут реагировать на измененную ракету как на обычную, если мы не создадим им особые стейты боли и смерти. Но этот объект будет реагировать по-особому, потому что у него нужные стейты есть. Из-за высокого пейншанса он сразу попадает в стейт боли и через goto – в стейт смерти.

А там работает команда ACS_execute(scriptnumber), которую мы уже проходили – активирует скрипт. На созданной мной карте, где этот актор Crack находится, есть скрипт 4, который с помощью опускания кусков стены создает эффект появления дыры. Активируется этот скрипт только если уничтожается актор Crack, а актор Crack уничтожается только если по нему попал проджектайл у которого damagetype blow, а этот демеджтайп есть только у ракеты (точнее у Cracker, который заменяет ракету). Сам объект Crack я устанавливаю на карте прямо рядом с нужным местом в стене (Crack, напоминаю, сам невидим), а на стене делаю текстуру трещины. Вот таким образом создан эффект взрываемой стены из Duke Nukem 3D.

 

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

 

Проджектайл-граната типичен для многих модов, а также встречается во многих других играх – например гранатомет в Shadow Warrior. Или метательные гранаты в Duke Nukem 3D.

Первое, что сделает проджектайл гранатой – флаг –NOGRAVITY, чтобы он падал на пол. Затем +DOOMBOUNCE заставит проджектайл прыгать от стен и пола. Он будет прыгать, пока не потеряет часть ускорения, затем уляжется спокойно. Что происходит в это момент? В это момент проджектайл входит в Death стейт (до этого он все еще находится в Spawn), и нам надо создать анимацию взрыва.

Но еще нам нужно две проперти – bouncefactor # и bouncecount #. Их мы рассматривали в разделе про проперти акторов. Боунсфактор управляет силой отскока, по умолчанию равен 0.7, мы можем уменьшить или увеличить, так что граната будет прыгать как эластичный попрыгунчик. Боунскаунт по умолчанию не используется, но мы можем ограничить число раз, которое проджектайл может отскочить от стен/пола – в некоторых случаях это полезно. Если мы не будем ограничивать число раз, то с флагом +DOOMBOUNCE проджектайл все равно потеряет ускорение и в скором времени уничтожится.

Не забывайте, что при использовании +HEXENBOUNCE и +HERETICBOUNCE акторы не будут терять ускорение. Поэтому для реалистичных гранат лучше использовать +DOOMBOUNCE.

Попробуем превратить нашу думовскую ракетницу в гранатомет. Но для этого вам придется нарисовать еще спрайты. Зачем? Если ваша граната продолговатой формы, вам нужно нарисовать 6-8 картинок того, как она крутится в полете, после того, как вылетела из ствола. Самое простое – взять картинку думовской ракеты, повернутой вбок, стереть у нее огонь из зада и сделать несколько картинок, каждый раз поворачивая ракету на 45 градусов – получится полная анимация вращения гранаты по кругу. Это легко сделать в Фотошопе.

Теперь перейдем к созданию:

 

DECORATE

Actor Grenade : Rocket replaces Rocket

{

-NOGRAVITY //теперь граната будет лететь вперед и падать

+DOOMBOUNCE //и отскакивать от стен и пола

bouncefactor 0.5 //мы сделали небольшую силу отскока

obituary "%o was blown up by %k's grenade." //мы меняем текст, ведь это уже не ракета

-ROCKETTRAIL //мы вырубаем ракетный след

+GRENADETRAIL //и врубаем след гранаты

seesound "weapons/grenade/bounce" //а seesound в этом случае управляет звуком отскока, поэтому делаем новый

states

{

spawn:

MISL ABCDEFGH 2 //эти созданные нами кадры – анимация крутящейся в полете гранаты

loop

death:

MISL A 35 //перед тем, как взорваться, граната целую секунду (ровно 35 тиков) лежит неподвижно

BOOM A bright 2 A_explode //и затем взрывается, поскольку мы ничего не меняли – с силой обычной ракеты

BOOM BCD bright 2 //анимация взрыва

stop

}

 

Тут надо сделать пару замечаний. Если у обычного проджектайла seesound "soundname" указывает звук, с которым он появляется из ствола, то у гранаты (проджектайла с флагом +DOOMBOUNCE) эта проперти обозначает звук,  с которым проджектайл будет отскакивать от стен и пола. А звук, с которым оружие будет выстреливать этой гранатой мы обозначим уже в анимации оружия через A_PlaySound("soundname") – об этом подробнее в разделе Weapon.

Во-вторых, мы сделали новые спрайты взрыва – предположим это BOOMA0, BOOMB0, BOOMC0, BOOMD0. ВАЖНО! Вы уже заметили, что мы можем писать кадры с одним названием спрайта друг за другом (как мы написали BOOM BCD 2). Но, имейте в виду, если мы к кадрам привязываем команду, она срабатывает столько раз, к скольки кадрам она привязана. Поэтому, если бы я написал BOOM ABCD 2 A_Explode, произошло бы четыре взрыва, по одному на каждый кадр. Поэтому их надо отделять друг от друга.

Итак, мы сделали гранату. Это простая вещь. А если нам хочется сделать гранату, которая не будет взрываться, пока мы не нажмем кнопочку, как в Blood или Duke Nukem 3D? Это уже сложнее, тут надо работать с анимацией оружия, поэтому это мы обсудим в Weapon.

Тут надо сделать еще одну заметку. Допустим, указанный нами кадр MISLG0 – это картинка лежащей гранаты. Но ведь граната может лечь не в одно и то же положение, а по-разному? Это мы можем организовать. Для начала нарисуйте около 4 спрайтов лежащей гранаты – просто под разным углом. Затем мы немного переделаем нашу гранату:

 

DECORATE

Actor Grenade : Rocket replaces Rocket

{

-NOGRAVITY

+DOOMBOUNCE

bouncefactor 0.5

obituary "%o was blown up by %k's grenade."

-ROCKETTRAIL

+GRENADETRAIL

seesound "weapons/grenade/bounce"

states

{

Spawn:

    MISL ABCDEFGH 2

    loop

Death:

    TNT1 A 0

    TNT1 A 0 A_Jump(128,6)

    TNT1 A 0 A_Jump(128,4)

    TNT1 A 0 A_Jump(128,2)

    MISL A 35 //первый вариант лежащей гранаты

    goto Explosion

    MISL D 35 //второй вариант лежащей гранаты

    goto Explosion

    MISL I 35 //третий вариант лежащей гранаты

    goto Explosion

    MISL J 35 //четвертый вариант лежащей гранаты

    goto Explosion

Explosion:

    BOOM A 2 bright A_explode

    BOOM BCD 2 bright

    stop

}

 

Что мы сделали? Мы нарисовали два спрайта - MISLI0 и MISLJ0 – лежащей гранаты – а также добавили к ним уже сделанные нами MISLA0 и MISLE0 (предположим, что они изображают гранату в подходящем ракурсе и для лежащей картинки). Мы также создали отдельный новый стейт Explosion, в котором проигрывается сама анимация взрыва – так намного удобнее. Затем мы реализовали четыре варианта лежащей гранаты с помощью прыжков через кадры – игра проходит через эти команды и каждый раз с 50% шансом (128 из 256) либо прыгает вперед, на другой кадр лежащей гранаты, либо идет дальше – получается четыре варианта лежащей гранаты, которая через секунду (35 тиков) переходит к стейту Explosion.

Тут заметьте несколько важных вещей:

1) поскольку у вас четыре варианта лежащего спрайта, вам нужно сделать тольк три команды прыжка – не увлекайтесь, если сделаете четвертую, прыжок будет вести за пределы стейта, и игра скорее всего не запустится;

2) команда A_Jump (а также все ей близкие – A_JumpIfInventory, A_JumpIfNoAmmo и т.п.) перепрыгивает НЕ СТРОКИ, а именно кадры, поэтому, если в строке указано три кадра, это именно три кадра, и в прыжке надо будет указать соответствующее количество;

3) прыжковые команды не учитывают другие кадры нулевой длины, к которым привязаны другие прыжки, именно поэтому мы видим, что прыжки командуют прыгать на 6, 4 или 2 кадра вперед, не учитывая самих себя – нулевые кадры, к которым и привязаны эти команды.

 

По желанию, вместо оффсета на число кадров, как мы помним, можно указывать прыжок прямо в стейт с нужным названием. Например так:

 

DECORATE

Actor Grenade : Rocket replaces Rocket

{

-NOGRAVITY

+DOOMBOUNCE

bouncefactor 0.5

obituary "%o was blown up by %k's grenade."

-ROCKETTRAIL

+GRENADETRAIL

seesound "weapons/grenade/bounce"

states

{

Spawn:

    MISL ABCDEFG 2

    loop

Death:

    TNT1 A 0

    TNT1 A 0 A_Jump(128, "death1")

    TNT1 A 0 A_Jump(128, "death2")

    TNT1 A 0 A_Jump(128, "death3")

    MISL A 35

    BOOM A 2 bright A_explode

    BOOM BCD 2 bright

    stop

Death1:

    MISL C 35

    BOOM A 2 bright A_explode

    BOOM BCD 2 bright

    stop

Death2:

    MISL I 35

    BOOM A 2 bright A_explode

    BOOM BCD 2 bright

    stop

Death3:

    MISL J 35

    BOOM A 2 bright A_explode

    BOOM BCD 2 bright

    stop

}

 

 

Сделаю еще одно замечание насчет флага +DOOMBOUNCE. Если мы его не вставим, проджектайл не будет отскакивать от стен и, упав на пол (то есть, если мы поставили –NOGRAVITY), сразу войдет в стейт Death. Таким образом можно сделать не отскакивающую гранату, либо еще как-нибудь это использовать.

 

Итак, мы сделали гранату. Внимательно изучите все написанное.

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

 

Если мы хотим, чтобы проджектайл был обычным, не как граната, но отражался от стен и пола, нам лучше использовать +HEXENBOUNCE – с этим флагом актор не будет терять ускорения. Но тогда нам стоит ограничить число возможных отскоков с помощью bouncecount #.

 

DECORATE

Actor ReflectiveRocket : Rocket replaces Rocket

{

+HEXENBOUNCE

bouncecount 3

}

 

Вот и все. Теперь ракета будет отскакивать от стен и пола, но не более 3-х раз. На 4-й раз взорвется.

 

 

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

Поскольку проджектайл – это полноценный актор, он сам может стрелять проджектайлами через A_CustomMissile, как монстры. Для чего это можно применять?

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

 

Другой вариант – нечто вроде оружия, что можно найти в Weapons Resource Wad. Там есть оружие типа БФГ9000, где шар от БФГ9000 медленно летит и стреляет вокруг себя зеленой плазмой (со спрайтом плазмы арахнотрона, чтобы по цвету шару БФГ соответствовать). А также там есть оружие типа БФГ, которое стреляет шаром, который, взрываясь, разбрасывает вокруг себя плазму. Как же это сделать?

Тут нам надо обратиться к A_CustomMissile:

A_CustomMissile("Missilename", spawnheight, Xoffset, Xangle, aimmode, Yangle) – актор (обычно монстр) стреляет проджектайлом (снарядом) Missilename, который появляется на высоте spawnheight от нижнего конца актора, может быть сдвинут по горизонтали от центра актора на Xoffset, может быть выпущен с горизонтальным отклонением Xangle (в градусах) от точки, куда смотрит монстр. В дополнение можно использовать еще два параметра – aimmode и Yangle. Aimmode может быть 0 – снаряд направляется в цель монстра (таково значение по умолчанию), 1 – снаряд автоматически направляется параллельно абстрактному снаряду с высотой спауна 32 и горизонтальном отклоном 0 (смысла я в этом режиме не вижу, хотя его можно использовать для создания монстра, стреляюещго большим числом ракет, но я предпочитаю делать это через код самого монстра) или 2 – в этом случае снаряд воспринимает последний параметр – Yangle – который обозначает вертикальное отклонение (в градусах) от прямой линии, по котрой идет взгляд монстра. Все параметры, кроме названия проджектайла – цифры, причем могут быть и отрицательными, и положительными. Если вы не хотите использовать параметры, пропишите нули, а последние два параметра можно вообще не писать.

Обратите внимание на четвертую позицию – угол. Это горизонтельное отклонение от луча направления полета снаряда. Позволим себе модифицировать снаряд импа, чтобы он стрелял во все стороны плазмой арахнотрона:

 

DECORATE

actor DeadlyBall : DoomImpBall replaces DoomImpBall //мы заменяем шар обычного импа

{

speed 5

states

  {

  Spawn:

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,0)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,10)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,20)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,30)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,40)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,50)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,60)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,70)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,80)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,90)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,100)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,110)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,120)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,130)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,140)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,150)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,160)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,170)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,180)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,190)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,200)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,210)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,220)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,230)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,240)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,250)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,260)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,270)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,280)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,290)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,300)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,310)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,320)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,330)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,340)

    BAL1 A 2 A_CustomMissile("Arachnotronplasma",0,0,350)

    loop

  }

}

 

Что это мы тут сделали? А то, что каждые 2 тика этот шар запускает плазму арахнотрона, а потом еще одну с наклоном на 10 градусов и т.д. То есть, летит себе медленный шар (скорость-то всего 5) и стреляет плазмой, причем точка выхода плазмы как будто плавно вращается внутри шара вокруг его оси. Выглядит, кстати, очень даже симпатично.

А можно вставить вместо плазмы арахнотрона шары того же импа. Чтобы шар импа выпускал вокруг себя другие шары импа. Правда тогда возникнет проблема: нельзя будет делать replace, иначе шары импа, которые будет выпускать наш проджектайл, тоже будут заменяться этим проджектайлом из-за реплейса, и они начнут бесконечно друг друга спаунить. Это нам не подходит, поэтому придется переписать стейт Missile у импа, вставив туда A_CustomMissile("DeadlyBall",16,0,0) – 16 это высота спауна, чтобы шар появлялся не у ног импа. Правда, не стоит забывать, что команда, управляющая стандартной атакой импа – A_TroopAttack – в таком случае перестанет корректно действовать, т.е. имп перестанет использовать свою атаку ближнего боя вблизи, а будет швырять снаряд на любом расстоянии. Поэтому нам придется вместо этого воспользоваться A_CustomComboAttack.

Короче, все это будет выглядеть так:

 

DECORATE

actor DeadlyBall : DoomImpBall

{

speed 5

states

  {

  Spawn:

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,0)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,10)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,20)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,30)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,40)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,50)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,60)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,70)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,80)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,90)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,100)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,110)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,120)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,130)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,140)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,150)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,160)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,170)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,180)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,190)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,200)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,210)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,220)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,230)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,240)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,250)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,260)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,270)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,280)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,290)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,300)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,310)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,320)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,330)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,340)

    BAL1 A 2 A_CustomMissile("DoomImpBall",0,0,350)

    loop

  }

}

 

actor NewDoomImp : DoomImp replaces DoomImp

{

states

{

Missile:

    TROO EF 8 A_FaceTarget

    TROO G 6 A_CustomComboAttack("DeadlyBall",16,3,"imp/melee") //вот так имп будет либо стрелять, либо, как и раньше, бить с силой 3

    goto See

}

 

Вот так это все будет работать. Правда есть одна проблема. Этот имп будет убивать сам себя. Потому что выстреливаемые дополнительные проджектайлы (плазма, или шары импа, или что угодно) будут уничтожать его самого (и всех, до кого доберутся). Хотя, конечно, можно создать для этих шаров отдельный damagetype и поставить у импа damagefactor от них на 0 – о чем, собственно, я уже писал выше, объясняя про типы демеджа.

Но, на мой взгляд, все это больше имеет смысл использовать для своих собственных проджектайлов (тех, которыми мы стреляем). Например, сделать подобие того самого оружия (GyroBuster) из Weapons Resource Wad. Сделаем так:

 

DECORATE

actor DeadlyBFGBall : BFGBall replaces BFGBall

{

speed 4

states

  {

  Spawn:

    BFS1 ABABABAB 4

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,0)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,10)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,20)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,30)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,40)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,50)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,60)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,70)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,80)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,90)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,100)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,110)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,120)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,130)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,140)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,150)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,160)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,170)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,180)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,190)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,200)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,210)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,220)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,230)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,240)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,250)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,260)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,270)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,280)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,290)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,300)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,310)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,320)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,330)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,340)

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,350)

    goto spawn+8

  }

}

 

 

Вот так вот. Надо кое-что отметить. В начале анимации такого снаряда надо вставить несколько кадров без стрельбы. Почему? Потому что если он сразу начнет запускать другие проджектайлы, они сильно побьют самого игрока, а если чуть-чуть подождать и скомандовать начинать стрелять где-то через пару метров (у меня два кадра повторяются четыре раза по 4 тика на каждый, в сумме 32 тика, это почти целая секунда полета проджектайла без спауна вторичных проджектайлов), то они уже не будут ранить игрока, даже попадая по нему. Поэтому в конце я вставляю не LOOP, а GOTO SPAWN+8, чтобы, пройдя всю анимацию, игра перескочила не на самое начало стейта, а уже прямо на кадры, где идет запуск вторичных проджектайлов.

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

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

 

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

 

DECORATE

actor DeadlyBFGBall : BFGBall replaces BFGBall

{

speed 4

states

  {

  Spawn:

    BFS1 ABABABAB 4

    BFS1 A 2 A_CustomMissile("ArachnotronPlasma",10,0,random(0,320))

    goto spawn+8

  }

}

 

 

А при желании можно сделать проджектайл, который будет без перерыва стрелять вторичными проджектайлами во все стороны разом. Как это сделать? Да просто. Возьмите код DeadlyBFGBall, где идет плавная стрельба во все стороны, и поставьте всем кадрам, кроме последнего, длительность 0. Тогда они все будут активироваться одновременно. С этим вы справитесь и без текстового примера.

 

Все эти же команды можно переместить и в стейт Death. Тогда при взрыве проджектайл будет стрелять другими проджектайлами – так, например, можно создать эффект осколочной гранаты. Мы можем реализовать это так:

 

DECORATE

Actor Grenade : Rocket replaces Rocket //уже сделанная нами граната

{

-NOGRAVITY

+DOOMBOUNCE

bouncefactor 0.5

obituary "%o was blown up by %k's grenade."

-ROCKETTRAIL

+GRENADETRAIL

seesound "weapons/grenade/bounce"

states

{

spawn:

    MISL ABCDEFGH 2

    loop

death:

    TNT1 A 0

    TNT1 A 0 A_Jump(128,6)

    TNT1 A 0 A_Jump(128,4)

    TNT1 A 0 A_Jump(128,2)

    MISL A 35

    goto Explosion

    MISL D 35

    goto Explosion

    MISL I 35

    goto Explosion

    MISL J 35

    goto Explosion

explosion:

    TNT1 A 0

    TNT1 A 0 A_explode

    TNT1 AAAAAAAAAAAAAAAAAAAAAAAAAAAA 0 A_custommissile("GrenadePiece",8,0,random(0,320),2,20)

    BOOM ABCD 2 bright

    stop

}

}

 

Actor GrenadePiece //осколок от гранаты

{

obituary "%o was ripper apart by %k's grenade." //при смерти игрока от осколков, а не самой гранаты, будет выводиться отдельное сообщение

projectile

radius 1

height 1

damage 6

-nogravity

+doombounce

+grenadetrail

+ripper

speed 15

seesound "weapons/grenade/piecebounce"

states

{

spawn:

    TNT1 A 1

    loop

death:

    TNT1 A 0

    stop

}

}

 

Итак, при взрыве граната расшвыривает 12 GrenadePiece. Вы заметили, что я не сделал спрайта для GrenadePiece – подобное можно было увидеть в моде "ww-diaz". Фишка в том, что мы будем видеть след (+grenadetrail) от летящих осколков, а сами они как бы такие маленькие, что их не видно. Это не помешает им ранить врагов, заодно прошивая их насквозь (+ripper). Желательно сделать отдельный звук для звона скочущих осколков. bouncefactor я не указал, оставив по умолчанию (0.7). Правда, боюсь, такая граната будет ранить своими осколками и игрока.

Вылет осколков происходит чуть-чуть выше точки взрыва гранаты (высота 8), и я использую aimmode 2, которое позволяет выпускать снаряды под вериткальным углом, чтобы осколки вылетали не вбок, а под углом 20 градусов вверх от пола, так они разлетаются дальше и действуют эффективнее, как у настоящей гранаты.

Поскольку осколков много, я просто много раз повторил кадр в строке – команда автоматически привязывается к каждому кадру, так что осколков вылетит много.

 

Помимо создания повреждающих осколков, вы просто можете создать эффект, будто от ракеты/гранаты отлетают дымящиеся куски. Подобное я реализовал в своих модах "Kill the Pain" и "Beautiful Doom" (в последнем это также применяется на взрывающейся бочке с кислотой).

 

Есть еще один вариант – мы можем сделать летящий снаряд, из которого выпадают на землю и взрываются снаряды. Этакая веерная бомбардировка.

Это вы сможете сделать и самостоятельно. Просто надо будет поставить вторичным проджектайлами флаг –NOGRAVITY и не включать флаги боунса.

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

 

 

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

Дело в том, что, если мы привяжем к проджектайлу A_CustomRailGun, этот проджектайл будет стрелять не во врагов, а в игрока. На флаг +FRIENDLY проджектайл никак не реагирует. Поэтому такую вещь приходится делать в два шага: вначале мы спауним проджектайлом невидимых дружелюбных монстров, а они уже стреляют во врагов рейлганом.

Звучит просто, но есть одна проблемка: эти монстры останутся висеть в воздухе и, даже если они будут невидимые и неосязаемые (с флагом +NOBLOCKMAP), они будут очень сильно забивать память игры. Что же делать?

С одной стороны, можно было бы попробовать использовать A_KillChildren, но ведь проджектайл-спаунер – не монстр, так что это не сработает.

С другой, можно было попробовать прописать в spawn стейт "монстров" команду thing_changetid(#,$), которая дала бы им всем нужный id (TID, Thing Identification), а в какой-нибудь из стейтов проджектайла прописать thing_remove($), которая все бы их убрала. Но, увы, эта функция работает крайне глючно, так что лучше придумать, как спауненные объекты будут убирать себя сами. Я нашел способ.

Для начала, мы сделаем сам проджектайл. Сделаем его на основе шара от БФГ:

 

DECORATE

actor RailShooterBall : BFGBall replaces BFGBall

{

+ripper //чтобы шар пролетал сквозь всех монстров

speed 5 //и чтобы летел помедленнее

radius 1 //мы делаем ему размер поменьше – везде пролезет

height 1 //да и меньше размер – меньше проблем

states

      {

      Spawn:

            BFS1 AB 1 A_SpawnItem("RailShooter") //а вот так он будет запускать нужные нам объекты, которые потом будут стрелять

            loop

      Death:

            TNT1 A 0 A_SpawnItem("RailShooter") //и напоследок, уничтожаясь, шар выпускает еще 4 штуки таких же

            TNT1 A 0 A_SpawnItem("RailShooter")

            TNT1 A 0 A_SpawnItem("RailShooter")

            TNT1 A 0 A_SpawnItem("RailShooter")

            BFE1 ABCDEF 8 bright

            stop

      }

}

 

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

Никаких параметров к этой команде в данном случае вводить не нужно.

А теперь мы создаем сам RailShooter. В его создании нам помогает чудная команда A_JumpIfTargetInLOS(offset, fov).

A_JumpIfTargetInLOS("statename", angle) – эта команда проверяет, видит ли монстр какую-нибудь из своих потенциальных целей, при этом ширина его обзора равна angle (цифра, обозначающая градусы), и, если он видит цель, он перепрыгивает в стейт statename; fov можно не указывать, и тогда будет проверяться вообще наличие какой-либо потенциальной цели, которую не перекрывала бы стена или другое препятствие.

 

 

DECORATE

actor Railshooter

{

monster //не забываем, что это монстр

speed 0 //скорость перемещения у него конечно нулевая

radius 1 //и размер тоже лучше поменьше, хотя со SpawnItem все это безопасно

height 1

+friendly //не забываем, он должен быть нам дружелюбен

+nogravity //и он должен не падать

+float //и лучше навесить на него флаг летающего монстра

+noblockmap //а также флаг неосязаемости

+noblood //ну и на всякий случай убрать у него кровь

      states

      {

      spawn:

            TNT1 A 1

            TNT1 A 1 A_look //эту команду не стоит использовать на первом кадре, бывают глюки

            goto see

      see:

            TNT1 A 1 A_chase //в этот момент "монстр" ищет и находит или не находит цель

            TNT1 A 1 A_JumpIfTargetInLos("missile") //и если "монстр" нашел цель, он перепрыгивает в атакующий стейт "Missile"

            goto death //а если не нашел – сразу к стейту смерти

      missile:

            TNT1 A 1 A_facetarget //наводится на цель

            TNT1 A 1 A_customrailgun(50,0,darkgreen,lawngreen,0,0) //бьет зеленым лучом рейлгана 50 мощности (параметры вам знакомы)

            goto death //и после одного выстрела в любом случае идет к смерти

      death:

            TNT1 A 0 //просто исчезает

            stop

      }

}

 

Главной проблемой в создании подобного оружия была необходимость уничтожить RailShooter даже в том случае, когда он заспаунился там, где нет никаких целей. Я думал, что, при наличие A_Chase, он автоматически перепрыгнет в Missile, если есть цель, а если нет, то уничтожится, но я ошибался. Поэтому я использовал A_JumpIfTargetInLOS. Если есть цель – он по ней стреляет и исчезает, если нет – сразу исчезает через goto death.

Можно сделать низкую мощность выстрела – тогда оружие будет стрелять больше, и выглядеть это будет эффектнее (например 5, вместо 50).

 

Теперь поясняю главную суть этого оружия. Главное для нас, чтобы выстрелу предшествовал A_Chase. Если мы попробуем заставить сам проджектайл стрелять в монстров, или если мы пропишем выстрел в каком-нибудь кадре RailShooter'а ДО A_Chase – луч полетит в нас, либо, если в шестой позиции у A_customrailgun стоит 1, луч будет просто стрелять вперед. Из-за всего этого нам нужно, чтобы стрелком выступал дружелюбный монстр, которого мы временно спауним – игрок, не разбиравший наш Декорейт код об этом и не догадывается.

 

Итак, на этом все главное, что нужно знать про проджектайлы, вы знаете. Изучив приведенные мной примеры, вы сможете их сочетать и создавать новые интересные снаряды.

 

 

Weapon

<<СОДЕРЖАНИЕ>>         

 

Описание Weapon в ZDoom Wiki.

 

Предметы класса Weapon наиболее емки по широте их применения. Оружие в модах для (Г)ЗДума может быть каким угодно – простым, сложным (с перезарядкой и падающими гильзами), с десятками вариантов простых и сложных проджектайлов, единичных и многочисленных, с одной атакой и с двумя... Вариантов есть множество. Но, для начала, надо разобрать базисы. Первое: у оружия, помимо стандартных для итемов стейтов, есть свой набор уникальных.

Базовые стейты акторов класса Weapon:

 

Spawn – картинка лежащего оружия

Select – анимация доставания оружия

Deselect – анимация убирания оружия

Ready – анимация оружия в готовом состоянии

Fire – анимация стрельбы основной атакой

Hold – в этот стейт можно сделать прыжок из анимации стрельбы основной атакой (подробнее ниже)

AltFire – анимация стрельбы вторичной атакой

AltHold – в этот стейт можно сделать прыжок из анимации стрельбы вторичной атакой

Flash – в этом стейте создается эффект освещения ствола оружия от вспышки выстрела основной атакой (подробнее ниже)

AltFlash – в этом стейте создается эффект освещения ствола оружия от вспышки выстрела вторичной атакой

 

Также у Weapon есть свой набор проперти и флагов. К тому же, Weapon обладает уникальным набором команд:

 

A_FireBullets(Xspread,Yspread,numbullets,damage,"pufftype",useammo) – командует оружию выстрелить пулями, Xspread (цифра) – разброс по горизонтали, Yspread (цифра) – разброс по вертикали, numbullets (цифра, целое число) – количество вылетающих пуль, damage – повреждения каждой пули (формула damage*((random()%3)+1)), pufftype – вид облачка-следа от пули (можно не указывать, если вы не создавали свой специальный пафф, тогда будет стандартный), useammo (1 или 0) – тратить или не тратить патроны оружия.

A_FireCustomMissile("Missilename",Xangle,useammo,Xoffset, spawnheight) – командует оружию выстрелить проджектайлом Missilename с горизонтальным отклонением от направления стрельбы на угол Xangle (цифра), использует либо не использует патроны (useammo – 1 или 0), со смещением от центра экрана по горизонтали Xoffset (цифра), снаряд появится на высоте spawnheight (цифра) от точки стрельбы игрока (которая регулируется через player.attackzoffset в описании игрока). Смещение по горизонтали считается в условных единицах, отталкивайтесь от того, что ширина экрана равна 64 (32 вправо, 32 влево, вправо – положительные числа, влево – отрицательные).

A_CustomPunch(#,norandom, usenoammo, "pufftype") – командует оружию сделать ближнюю атаку с силой #, если norandom 1, то повреждения будут фиксированные (без формулы рандомизации), если usenoammo 1, то не будут использоваться патроны, также будет вызван дымок Pufftype (можно не указывать, тогда будет стандартный)

A_RailAttack(#,Xoffset, useammo, ringcolor, corecolor, silent) – командует оружию выстрелить (г)здумовским рейлганом с повреждением #, может быть смещен по горизонтали на Xoffset (цифра), если на месте useammo 1, то использует патроны оружия, имеет цвет кольца ringcolor и цвет луча corecolor, если silent 1, то выстрел бесшумный.

 

Как вы уже поняли, эти команды перекликаются с аналогичными командами, предназначенными для атак монстров – A_BulletAttack, A_CustomMissile, A_MelleAttack и A_CustomRailGun. Вы не должны их путать, к оружию нельзя привязывать команды атаки сторонних акторов, как нельзя делать и наоборот.

Теперь команды не связанные с атакой:

 

A_Raise – запустить анимацию подъема спрайта оружия из-за нижней части экрана, это НЕОБХОДИМО прописывать в стейте Select

A_Lower – запустить анимацию опускания спрайта оружия за нижнюю часть экрана, это НЕОБХОДИМО прописывать в стейте Deselect

A_WeaponReady – указание, что оружие находится в готовом к бою состоянии, это НЕОБХОДИМО прописывать в стейте Ready

A_Refire – когда анимация дойдет до этого момента, она возвратится к началу стейта (Fire в данном случае), так имитируется автоматическая стрельба; если есть стейт Hold, то анимация из стейта Fire перескочит в стейт Hold; даже без этой команды оружие будет вести автоматический огонь, если у него только не включен флаг +NOAUTOFIRE, однако оно будет повторять весь стейт Fire целиком, а с помощью этой команды вы можете ограничить точку, до которой будет повторяться стейт при автоматической стрельбе

A_GunFlash – отсылает к анимации вспышки выстрела (стейту Flash или AltFlash) (подробнее ниже)

 

Разумеется, к оружию можно (и придется) привязывать и другие команды, в частности всякие команды перепрыгивания через стейты и выдачи инвентарных предметов.

 

К тому же, оружие обладает набором уникальных флагов, и некоторые из них нужно отметить:

 

+NOAUTOFIRE – оружие не начнет стрелять, если вы подняли его, удерживая кнопку огня, также оно не будет стрелять автоматически, если только на одном из кадров в стейте Fire не использована команда A_ReFire

+DONTBOB – спрайт оружия на экране не болтается во время ходьбы

+NOALERT – атака этим оружием не издает звука (чисто технически, сам же выбранный звук из колонок будет звучать как обычно), монстры на нее не реагируют

+PRIMARY_USES_BOTH – оружие основной атакой тратит оба вида патронов, привязанных к нему (как последнее оружие в Hexen)

+WIMPY_WEAPON – обозначает оружие как очень слабое, если игрок находит что-то другое, сразу выбирает другое оружие

+POWERED_UP – это оружие вызывается пауэр-апами типа "WeaponLevel2", это мы обсудим позже

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

+CHEATNOTWEAPON – это оружие не будет даваться игроку при наборе читов idkfa, idfa, give all, give <weaponname>

 

Перед названием флага после +/- можно добавить WEAPON., как вы догадались, это префикс для оружейных флагов, его, как и все префиксы, можно не писать.

Остальные флаги смотреть в ZDoom Wiki.

 

Для наглядности мы разберем код обычного думовского пистолета:

 

DECORATE

actor Pistol : Weapon

{

  obituary "%o was tickled by %k's pea shooter." //эта проперти управляет сообщением для десматча, которое объясняет, кто кого чем и как

  attacksound "weapons/pistol"

  weapon.selectionorder 1900 //эта проперти показывает значимость оружия, чем меньше номер, тем раньше будет оружие стоять в линейке выбора

  weapon.kickback 100 //эта проперти управляет силой, с которой оружие отбрасывает врагов

  weapon.ammotype "Clip" //эта проперти указывает патроны, использующиеся оружием для основной атаки

  weapon.ammouse 1 //сколько патронов оружие использует за одну основную атаку

  weapon.ammogive 20 //сколько патронов мы получаем вместе с оружием, когда находим его (для пистолета он реально не нужен, ведь он есть сразу)

  +WEAPON.WIMPY_WEAPON //флаг обозначения слабого оружия (см. выше)

  states

  {

  Ready:

    PISG A 1 A_WeaponReady //оружие готово; НИКОГДА НЕ ЗАБЫВАЙТЕ ПРО ЭТУ КОМАНДУ В ЭТОМ СТЕЙТЕ

    loop //анимация бесконечно повторяется, пока мы не начнем стрелять и не перейдем в стейт Fire

  Deselect:

    PISG A 1 A_Lower //спрайт оружия опускается; НИКОГДА НЕ ЗАБЫВАЙТЕ ПРО ЭТУ КОМАНДУ В ЭТОМ СТЕЙТЕ

    loop //анимация бесконечно повторяется, пока спрайт не исчезнет за низом экрана

  Select:

    PISG A 1 A_Raise //спрайт оружия поднимается; НИКОГДА НЕ ЗАБЫВАЙТЕ ПРО ЭТУ КОМАНДУ В ЭТОМ СТЕЙТЕ

    loop //анимация бесконечно повторяется, пока спрайт не поднимется на нужную высоту

  Fire:

    PISG A 4

    PISG B 0 A_FireBullets (5.6, 0, 1, 5, "BulletPuff") //пистолет стреляет, разброс 5.6-0, стреляет одной пулей с силой 5

    PISG B 6 A_GunFlash

    PISG C 4

    PISG B 5 A_ReFire //если мы не отпустили кнопку огня, возвращается к началу стейта Fire, чтобы выстрелить еще раз

    goto Ready //если мы отпустили кнопку огня, отправляется к стейту Ready

  Flash:

    PISF A 7 bright A_Light1

    PISF A 0 bright A_Light0

    stop

  }

}

 

 

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

 

Теперь комментарии:

obituary "messagetext" – сообщение, которое в десматче показывает кто кого чем и как, вместо %o в игре появится имя убитого, вместо %k имя убийцы, об этом мы уже говорили

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

weapon.ammotype "ammoname" – отсылка к итему класса Ammo, который надо вписать в Декорейт до или после кода оружия

 

Заметьте, что количество патронов, указанное в weapon.ammogive мы будем получать только из оружия, которое лежит само по себе. А если мы будем подбирать оружие после выпадения его из врага, в нем будет в два раза меньшее число патронов (разумеется, оно будет округляться, если базовое число нечетное).

 

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

 

Также надо отметить не упомянутые здесь проперти:

weapon.upsound "soundname" – звук, который звучит при выборе оружия (играет в Select стейте)

weapon.readysound "soundname" – звук, который звучит в готовом состояни (Ready), например какое-нибудь гудение плазменной пушки и т.п.

 

Существует способ ускорить анимацию поднимания и опускания оружия, если есть такая нужда. Для этого надо добавить в стейты Select и Deselect в начале кадр нулевой длины:

 

DECORATE

actor Pistol1 : Pistol replaces Pistol //он будет замещать старый пистолет, частично копируя его свойства

{

  states

  {

  Deselect:

    PISG A 0 A_Lower

    PISG A 1 A_Lower

    loop

  Select:

    PISG A 0 A_Raise

    PISG A 1 A_Raise

    loop

  }

}

 

 

Какой именно кадр вы добавляете – как и со всеми нулевыми кадрами, значения не имеет. Если увеличить число нулевых кадров, анимация будет еще быстрее. Вставки десяти нулевых кадров хватит, чтобы полностью визуально убрать анимацию Select-Deselect.

 

Теперь лирическое отступление про A_GunFlash. Дело в том, что, если вы вскроете вад одного из оригинальных Думов, вы обнаружите, что на спрайтах экранного оружия нет изображения вспышек от выстрела, а вспышки нарисованы отдельно. Смысл, видимо, был только в экономии места, потому что таким образом мы можем один и тот же спрайт использовать и в стейтах Ready, Select, Deselect и его же использовать в Fire, наложив на него рисунок вспышки.

Поэтому, в данном случае, спрайт PISFA0 – это вспышка. Команда A_GunFlash вызывает стейт Flash (если A_GunFlash находится в стейте AltFire, то она вызывает AltFlash), и спрайты из него накладываются на спрайты в продолжающемся стейте Fire. Команды A_Light1 и A_Light0 создают две степени увеличения яркости спрайта оружия, чтобы создать эффект освещения оружия от вспышки выстрела.

В современных модах всем этим пренебрегают и делают спрайт вместе с рисунком вспышки и отдельно спрайт без вспышки, но стейтом Flash все равно часто пользуются, только привязывают команды A_Light1 и A_Light0 к пустым спрайтам TNT1A0.

 

Однако, думовский пистолет – оружие простое, поэтому мы добавим еще один пример. Думовский плазмаган:

 

DECORATE

actor PlasmaRifle : Weapon 2004

{

  spawnid 30

  radius 20

  height 16

  inventory.pickupmessage "You got the plasma gun!"

  weapon.selectionorder 100

  weapon.kickback 100

  weapon.ammotype "Cell"

  weapon.ammouse 1

  weapon.ammogive 40

  states

  {

  Ready:

    PLSG A 1 A_WeaponReady

    loop

  Deselect:

    PLSG A 1 A_Lower

    loop

  Select:

    PLSG A 1 A_Raise

    loop

  Fire:

    PLSG A 0 A_FireCustomMissile ("PlasmaBall")

    PLSG A 3 A_GunFlash

    PLSG B 20 A_ReFire

    goto Ready

  Flash:

    PLSF A 0 bright A_Jump (128, 3) // --.

    PLSF A 4 bright A_Light1 //          |

    PLSF A 0 bright A_Light0 //          |

    stop                     //          |

    PLSF B 4 bright A_Light1 // ß-------`

    PLSF B 0 bright A_Light0

    stop

  Spawn:

    PLAS A -1 //эта картинка также будет и инвентарной иконкой, потомучто в Weapon все равно не работает inventory.icon

    stop

  }

}

 

Тут обратите внимание на функцию, которую выполняет A_Refire. Если мы зажимаем кнопку огня, то кадр, к которому привязан A_ReFire НЕ ПОКАЗЫВАЕТСЯ, а если мы уже отпустили кнопку огня – показывается. Засчет этого возникает особый эффект плазмагана: когда мы перестаем стрелять, мы видим один кадр, где думер держит плазмаган поднятым, будто остужает ствол. Если бы были какие-то кадры ПОСЛЕ этого кадра, они тоже были бы показаны после того, как мы отпустим кнопку огня.

Как вы можете заметить, A_FireCustomMissile здесь всего лишь обозначает сам проджектайл – PlasmaBall. Ничего больше указывать не нужно – проверьте параметры этой команды, и вы сами поймете, что это так. Если вас беспокоит вопрос useammo, то скажу, что по умолчанию патроны при использовании этой команды тратятся в количестве один к одному (один патро на один выпущенный снаряд).

Еще здесь используется A_Jump, который с шансом 50% (128 из 256) перепрыгивает через два кадра и строчку stop и попадает во вторую часть стейта Flash (я позволил себе показать это с помощью псевдографической стрелки). Зачем? Да просто для красоты – чтобы было два вида вспышки. Но это не так важно. Главное, что сейчас вы видите элемент использования джамп-команд, и это очень важно. Заметьте, что цифра обозначает пропускаемые кадры, включая также команды stop, loop и goto.

 

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

 

DECORATE

actor NewPistol : Pistol replaces Pistol //мы заменяем стандартный пистолет новым

{

  weapon.ammogive 40 //и решаем, что патронов с этим оружием нам дается больше

  -WEAPON.WIMPY_WEAPON //убираем этот флаг, т.к. теперь мы считаем, что наше оружие стало круче

  weapon.ammotype2   "Cell" //а это мы назначаем патроны для вторичной атаки, проперти все те же, только цифра 2 на конце; он использует Cell

  weapon.ammogive2   20 //и дает 20 Cell при взятии его

  weapon.ammouse2    1 //использует 1 Cell за одну вторичную атаку

  states //как я уже замечал в описании проджектайлов, нам не надо заново писать все стейты при заимствовании, а только те, которые мы изменяем

  {

  Fire:

    PISG A 4

    PISG B 0 A_FireBullets (5, 5, 1, 10, "BulletPuff") //изменяем параметры разброса и убойной силы

    PISG B 6 A_GunFlash

    PISG C 4

    PISG B 5 A_ReFire

    goto Ready

  AltFire: //а тут мы назначаем альтернативную атаку

    PISG A 4

    PISG B 0 A_GunFlash //мы можем поместить A_GunFlash в нулевой кадр перед выстрелом

    PISG B 6 A_RailAttack(10, 0, 1, blue, red) //а сам выстрел наоборот сделать не нулевой длины (сравните со стейтом Fire), и мы делаем RailAttack

    PISG C 4

    PISG B 5 A_ReFire

    goto Ready

  AltFlash: //поскольку мы добавили AltFire, нам нужен и AltFlash

    NULL A 7 bright A_Light1 //но мы решаем убрать спрайт вспышки выстрела у вторичной атаки, так что рельсой пистолет будет стрелять без вспышки

    NULL A 0 bright A_Light0 //но мы оставили команды освещения, так что спрайт будет освещен, хотя спрайта вспышки не будет

    stop

  }

}

 

 

KEYCONF

weaponsection "NewPistolMod"

 

setslot 2 NewPistol

 

 

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

Также не забывайте, что игрок не сразу появится в руках с этим пистолетом. Вы сможете, конечно, набрать give all и нажать кнопку 2, тогда выберется новый пистолет (а старый выбрать уже будет нельзя, потому что на его слоте находится только новый, а старый мы убрали), но он не будет в руках игрока сразу. Чтобы он появился с пистолетом, вам придется внести дополнительные изменения в Декорейт и Кейконф:

 

DECORATE

actor Doomer : DoomPlayer //создаем нового игрока, почти точную копию старого, но...

{

player.startitem "NewPistol",1 //мы добавляем наш новый пистолет в стартовые итемы игрока

player.startitem "Cell",50 //ну и заодно добавляем Cell, чтобы сразу можно было использовать вторичную атаку

player.startitem "Clip",50 //мы должны заново вписать их, иначе они не появятся

player.startitem "Fist" //и кулак тоже

}

 

 

Примечание: когда вы создаете новый класс игрока, даже если вы создаете его с частичным заимствованием, все стандартные параметры его стартовыих итемов обнуляются, поэтому к вашим новым вам также придется приписать и старые – Fist, Clip 50 – конечно, если вы сами не создали новый кулак и новые патроны.

 

KEYCONF

 

clearplayerclasses

 

addplayerclass Doomer //теперь вместо старого мы добавили нового игрока

 

 

Вот теперь все работает.

ИМЕЙТЕ В ВИДУ: не пытайтесь добавить новый класс игрока через replace новым старого – это не сработает. Вам все равно нужно добавлять его через KEYCONF.

Правда лично я при тестировании вышеописанного обнаружил, что основная атака почему-то стала бесшумной, хотя при заимствовании свойств наш новый пистолет должен был самостоятельно взять от старого его attacksound. Впрочем, лично я (да и не только я) вообще не прописываю в модах проперти со звуками. Вместо этого я прямо перед кадром с атакой вставляю нулевой кадр с командой A_PlaySound("soundname"). Таким образом:

 

DECORATE

actor NewPistol : Pistol replaces Pistol

{

  weapon.ammogive 40

  -WEAPON.WIMPY_WEAPON

  weapon.ammotype2   "Cell"

  weapon.ammogive2   20

  weapon.ammouse2    1

  states

  {

  Fire:

    PISG A 4

    NULL A 0 A_playsound("weapons/pistol") //вот здесь я командую озвучить нужный звук

    PISG B 0 A_FireBullets (5, 5, 1, 10, "BulletPuff")

    PISG B 6 A_GunFlash

    PISG C 4

    PISG B 5 A_ReFire

    goto Ready

  AltFire:

    PISG A 4

    PISG B 0 A_GunFlash

    PISG B 6 A_RailAttack(10, 0, 1, blue, red)

    PISG C 4

    PISG B 5 A_ReFire

    goto Ready

  AltFlash:

    NULL A 7 bright A_Light1

    NULL A 0 bright A_Light0

    stop

  }

}

 

 

Как вы заметили, какой спрайт вписан в нулевом кадре – не имеет никакого значения, вы ведь его не увидите. Я обычно вписываю NULLA0, некоторые везде пишут TNT1A0, это не имеет никакого значения. Это имеет значение с предметами (я уже говорил, что с ними NULLA0 использовать нельзя, даже в нулевых кадрах, если этот кадр идет первым в анимации Spawn).

Итак, мы создали простенькое новое оружие – более мощный пистолет, который в дополнение вторичной атакой может стрелять рейлганом. Неплохо? Неплохо, но будет еще лучше. Ведь мы хотим научиться делать оружие с перезарядкой.

 

Плюс новых версий (Г)ЗДума в том, что в них можно добавлять новые стейты с любыми именами и делать отсылки к этим стейтам через разные джампы и goto. Раньше создание многих вещей было возможно только с чудовищным числом джампов, но теперь все намного легче.

 

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

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

Гильза – это проджектайл, только он никого не бьет и не взрывается. Типичная гильза выглядит вот так:

 

DECORATE

ACTOR 9mmCasing

{

    Scale 0.25 //спрайт гильзы крупный, чтобы разрешение было высоким, поэтому в игре его надо уменьшить скейлом

    Radius 3

    Height 3

    Speed 6

   PROJECTILE

   +DOOMBOUNCE //главное – гильзы должны отскакивать, боунсфактор будем использовать по умолчанию

   -NOGRAVITY //разумеется, гильза подвержена гравитации

   -NOBLOCKMAP //и она может стукнуться и об другого актора и отскочить от него

   SeeSound "weapons/casing" //этот звук, как мы помним из урока про гранаты, упрваляет звуком при отскоке актора от стен и пола

   States

   {

   Spawn:

      CAS2 ABCDE 1 //здесь у нас анимация крутящейся в воздухе в полете гильзы

      loop

   Death:

      CAS2 E 1 //а здесь у нас картинка лежащей гильзы

      loop //стейт надо повторять, чтобы гильза продолжала лежать

   }

}

 

Мы проходили гранаты, так что многое нам тут знакомо. Эта гильза простая, у нее всего один лежачий спрайт, но, вспоминая гранаты, вы знаете, как сделать больше. Также замечу, что я предпочитаю гильзы, которые не исчезают, поэтому я ставлю в Death loop (или кадр длительности -1 и stop). Обычно их делают исчезаемыми, примерно так:

 

DECORATE

ACTOR 9mmCasing

{

...

   Death:

      CAS2 EEEEEEEEEE 35 //10 раз один и тот же кадр по 35 тиков, т.е. гильза лежит 10 секунд

      stop //а затем исчезает

...

}

 

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

 

Теперь надо, собственно, привязать эту гильзу к пистолету. Спауниться гильза будет через A_FireCustomMissile. Возьмемся опять за обычный думовский пистолет:

 

DECORATE

actor NewPistol : Pistol replaces Pistol

{

states

{

  Fire:

    PISG A 4

    PISG B 0 A_FireBullets (5.6, 0, 1, 5, "BulletPuff")

    PISG B 6 A_GunFlash

    NULL A 0 A_FireCustomMissile("9mmCasing",45,0,4) //мы привязываем вылет гильзы к анимации движения затвора, а не прямо к кадру выстрела

    PISG C 4

    PISG B 5 A_ReFire

    goto Ready

  }

}

 

ACTOR 9mmCasing

{

    Scale 0.25

    Radius 3

    Height 3

    Speed 6

   PROJECTILE

   +DOOMBOUNCE

   -NOGRAVITY

   -NOBLOCKMAP

   SeeSound "weapons/casing"

   States

   {

   Spawn:

      CAS2 ABCDE 1

      loop

   Death:

      CAS2 E 1

      loop

   }

}

 

 

Теперь посмотрим внимательно на то, как я запустил гильзу. Вторая позиция в A_FireCustomMissile – это угол, горизонтальный отклон от прямой линии, идущей из глаз игрока. Положительная цифра – отклон вправо. То есть гильза будет лететь вправо и вперед. В третьей позиции 0 – выстрел гильзой не расходует патронов. В четвертой позиции 4 – это смещение точки вылета снаряда по горизонтали, относительно центра экрана, я смещаю его чуть-чуть вправо.

 

Неплохо. Но есть один не очень радующий нас момент: гильза летит не вбок, а вперед и вбок, что нереалистично. А если мы сделаем угол наклона 90, она будет вылетать сбоку от нас, и мы не будем ее видеть в момент вылета, а это тоже нереалистично – ствол ведь как бы находится у нас в руках, перед лицом.

Что делать?

 

Для этого существует второй способ создавать гильзы, более сложный – в два шага.

Вначале мы создаем невидимый проджектайл, который бесшумно и незримо вылетает вперед из нашего оружия. Пролетев совсем крохотное расстояние (условно – чуть меньше расстояния вытянутой руки), он исчезает, перед этим выстрелив из себя гильзу вбок. Вот таким образом гильза возникнет перед нами и полетит вбок, эффект будет полностью реалистичный – как в Shadow Warrior, например.

Осмыслите написанное. Принцип прост. Приступим к исполнению:

 

DECORATE

ACTOR 9mmCasingSpawner //эта штука будет стрелять проджектайлом

{

    Radius 1

    Height 1

    Speed 20 //мы должны сделать проджектайл достаточно быстрым, потому что он должен успеть пролететь вперед некотрое расстояние...

    PROJECTILE

    States

    {

    Spawn:

        TNT1 A 1

        Goto Death //...прежде чем умрет

    Death:

        TNT1 A 0 A_CustomMissile("9mmCasing",-2,0,random(-80,-100),2,random(40,60)) //а вот и выстрел гильзой, она летит вправо

        Stop

    }

}

 

ACTOR 9mmCasing

{

    Scale 0.25

    Radius 3

    Height 3

    Speed 6

   PROJECTILE

   +DOOMBOUNCE

   -NOGRAVITY

   -NOBLOCKMAP

   SeeSound "weapons/casing"

   States

   {

   Spawn:

      CAS2 ABCDE 1

      loop

   Death:

      CAS2 E 1

      loop

   }

}

 

actor NewPistol : Pistol replaces Pistol

{

states

{

  Fire:

    PISG A 4

    PISG B 0 A_FireBullets (5.6, 0, 1, 5, "BulletPuff")

    PISG B 6 A_GunFlash

    NULL A 0 A_FireCustomMissile("9mmCasingSpawner",0,0,0) //здесь нам уже угол указывать не надо, только горизонтальное смещение по желанию

    PISG C 4

    PISG B 5 A_ReFire

    goto Ready

  }

}

 

 

 

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

Как вы заметили, я добавил в спаунер гильзы два довольно редко используемых параметра – aimmode и Yangle. Я делаю это для того, чтобы гильза летела не просто вбок, но еще и с некоторым углом вверх, так ее хорошо видно, и смотрится эффектно. Запомните, что надо указать aimmode – 2. Вертикальный угол будет измеряться в градусах. С помощью горизонатльного угла мы заставляем гильзу отклониться вправо на 80-100 градусов (с помощью рендома), а потом еще и вверх на 40-60 градусов. Получается как раз то, что надо.

Однако тут надо сделать важное замечение. В принципе, вы спокойно можете и не пользоваться вертикальным углом, так будет менее эффектно, но даже реалистичнее. ОДНАКО! Если вы не указываете aimmode 2, а заканчиваете параметры на указании горизонтального угла, то тут есть зависимость: по умолчанию отрицательные числа будут указывать горизонатльный наклон влево, а положительные – вправо; если же вы используете aimmode 2, отрицательные числа будут обозначать горизонтальный наклон ВПРАВО, а положительные ВЛЕВО. Гильза в моем примере вылетает в правую сторону, но, если бы я не пользоваться aimmode 2 и вертикальным угловым наклоном, я бы указал горизональный угол random(80,100), а не random(-80,-100).

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

 

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

 

 

С перезарядкой дело обстоит не намного сложнее. Поскольку (Г)ЗДум уже давно поддерживает создание собственных стейтов, все будет довольно просто (это в старые времена было трудно).

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

Не забывайте, при создании оружия с перезарядкой ему НЕОБХОДИМО поставить флаг +AMMO_OPTIONAL, иначе оружие не будет осуществлять перезарядку как надо.

Существует несколько способов сделать перезарядку. Во-первых, ее можно сделать через вторичную атаку, а можно создать через KEYCONF отдельную кнопку, отвечающую за перезарядку. Во-вторых, можно сделать перезарядку жесткую, когда неизрасходованные патроны в магазине будут теряться при перезарядке, а можно сделать щадящую, как в современных играх. Причем сделать щадящую труднее. В-третьих, мы можем вообще сделать перезарядку без кнопки – принудительную, как у пистолета в Duke Nukem 3D, но это несовременно, и этот вариант мы рассматривать не будем.

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

Для начала мы рассмотрим простой вариант – перезарядка через альтернативную атаку. Прежде всего, нам придется нарисовать новые спрайты – анимацию перезарядки. Предположим, мы их уже нарисовали. Переделаем уже модифицированный нами пистолет для примера:

 

DECORATE

actor NewPistol : Pistol replaces Pistol

{

weapon.ammotype   "PistolClip" //это – патроны в обойме

weapon.ammogive   0 //когда мы подбираем оружие, их нам не дают, ведь обойма при взятии оружия не заполняется

weapon.ammouse    1 //используется по 1-й штуке за выстрел

weapon.ammotype2  "ExtraPistolAmmo" //а эти патроны мы будем использовать при перезарядке, это запас

weapon.ammogive2  10 //допустим, нам дают их по 10 штук – будем считать, что такова емкость магазина

weapon.ammouse2   0 //но мы не будем указывать их расход, будем назначать его вручную дальше

+AMMO_OPTINAL //наличие этого флага гарантирует корректную работу перезарядки

states

{

  Fire:

    NULL A 0 A_JumpIfNoAmmo("AltFire") //если при входе в стейт атаки оружие обнаруживает отсутствие патронов, он прыгает в стейт перезарядки

    PISG A 4

    NULL A 0 A_playsound("weapons/pistol/fire") //проигрывает звук стрельбы

    PISG B 0 A_FireBullets (5.6, 0, 1, 5, "BulletPuff") //стреляет

    PISG B 6 A_GunFlash

    NULL A 0 A_FireCustomMissile("9mmCasingSpawner",0,0,0) //уже знакомый нам спаун гильзы

    PISG C 4

    PISG B 5 A_ReFire

    goto Ready

  AltFire:

    NULL A 0 A_JumpIfInventory("PistolClip",10,"Ready") //если мы нажимаем кнопку альтернативной атаки, а магазин полон, мы возвращаемся к Ready

    NULL A 0 A_JumpIfInventory("ExtraPistolAmmo",1,1) //а перезарядку мы начинаем только если у нас есть хотя бы один патрон в запасе ---.

    goto ready //а если патронов в запасе нет, мы возвращаемся к Ready                                                                   |

    PISR A 3 A_PlaySound("weapons/pistol/clipout") //предположим, что тут анимация выброса пустой обоймы, вставляем звук  <--------------'

    PISR BC 2                                    

    PISR D 7 A_PlaySound("weapons/pistol/clipin") //предположим, что тут анимация вставки новой обоймы, добавляем соответствующий звук

    PISR CBA 2

    NULL A 0 A_JumpIfInventory("ExtraPistolAmmo",1,1) //а дальше начнется проверка на наличие боеприпасов в кармане <------. и снова проверяет, есть ли

    goto ready                                        // и, если хоть один патрон нашелся,...                              | патроны, и, если есть,

    NULL A 0 A_TakeInventory("ExtraPistolAmmo",1) //у нас забирается один патрон из запаса                                 | отнимает патрон из запаса

    NULL A 0 A_GiveInventory("PistolClip",1) //и один вставляется в обойму                                                 | и дает еще один в обойму,

    NULL A 0 A_jumpifinventory("PistolClip",10,"ready") //если патронов уже 10, мы возвращаемся к Ready                    | пока их не станет в обойме

    goto AltFire+10 //а если патронов меньше 10, то мы прыгаем к началу анимации, но со смещением вперед – вот СЮДА -------` максимум, т.е. 10

  }

}

 

actor PistolClip : Ammo //это патроны, которые в обойме

{

inventory.maxamount 10 //это будет предельный размер обоймы, допустим 10

inventory.icon "PISTCLIP" //иконка, отображающая обойму, если ее сделать, игроку будет видно, сколько патронов у него в обойме (при Alternative Hud)

}

 

Actor ExtraPistolAmmo : Ammo //а это патроны запаса

{

inventory.amount 10 //за раз будем подбирать столько, сколько хватит, чтобы заполнить одну обойму

inventory.maxamount 200

inventory.pickupsound "pickups/pistolammo" //стоит указать звук подбирания

inventory.pickupmessage "Picked up pistol ammo." //и сообщение

inventory.icon "CLIPA0" //и у них-то инвентарную иконку точно стоит сделать

states

{

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

        CLIP A 1

        loop

}

}

 

 

Внимательно изучите написанное. Таков принцип перезарядки. Главное – постоянное возвращение к тому этапу, где идет проверка на наличие патронов в запасе, затем забирание из запаса и добавление в обойму. Поскольку эти кадры имеют нулевую длинну, в игре все вычисления будут происходить мгновенно.

Зачем нам это нужно, между прочим? Почему мы не можем просто сразу прописать команды – взять 10 патронов из запаса и вставить 10 в обойму? Да потому что в этом случае, даже если в кармане осталось всего 3 патрона, команда, отбирая эти три патрона, будет давать нам в обойму 10. Потому что зависимости между количеством отнятых и количеством даваемых не возникнет. Поэтому нам нужно постоянное возвращение к проверке.

Не забывайте, что мы будем подбирать из врагов (или другими способами) не те патроны, что в обойме, а те, что запасные.

 

В принципе, существует более простой способ, без всех этих проверок. Можно считать патроны запаса не патронами, а обоймами, т.е. каждый раз при перезарядке отнимать одну обойму и давать 10 патронов в обойму (причем не важно, сколько было в обойме, у нас ведь не может быть больше 10, установлен inventory.maxamount 10). Так выглядит упомянутый мной выше способ создания "жесткой" перезарядки. Но мы его рассматривать не будем.

 

Продолжая описание данного способа, замечу, что можно упросить код пистолета с помощью введения дополнительного стейта:

 

DECORATE

actor NewPistol : Pistol replaces Pistol

{

weapon.ammotype   "PistolClip"

weapon.ammogive   0

weapon.ammouse    1

weapon.ammotype2  "ExtraPistolAmmo"

weapon.ammogive2  10

weapon.ammouse2   0

+AMMO_OPTIONAL

states

{

  Fire:

    NULL A 0 A_JumpIfNoAmmo("AltFire")

    PISG A 4

    NULL A 0 A_playsound("weapons/pistol/fire")

    PISG B 0 A_FireBullets (5.6, 0, 1, 5, "BulletPuff")

    PISG B 6 A_GunFlash

    NULL A 0 A_FireCustomMissile("9mmCasingSpawner",0,0,0)

    PISG C 4

    PISG B 5 A_ReFire

    goto Ready

  AltFire:

    NULL A 0 A_JumpIfInventory("PistolClip",10,"Ready")

    NULL A 0 A_JumpIfInventory("ExtraPistolAmmo",1,1)

    goto ready

    PISR A 3 A_PlaySound("weapons/pistol/clipout")

    PISR BC 2                                    

    PISR D 7 A_PlaySound("weapons/pistol/clipin")

    PISR CBA 2

    goto AltFire1

  AltFire1:

    NULL A 0 A_JumpIfInventory("ExtraPistolAmmo",1,1) //<--.

    goto ready                                        //   |

    NULL A 0 A_TakeInventory("ExtraPistolAmmo",1) //       |

    NULL A 0 A_GiveInventory("PistolClip",1) //            |

    NULL A 0 A_jumpifinventory("PistolClip",10,"ready")//  |

    goto AltFire1 //---------------------------------------'

}

 

 

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

Я назвал этот стейт AltFire1 просто для примера, чтобы показать, что он логически следует после AltFire, но назвать этот стейт можно как угодно, "хоть WinniePooh" (© Guest). Зависит от принципов классификации, которыми пользуется создатель вада.

 

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

 

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

 

Перезарядка – вещь достаточно простая, однако есть, что добавить.

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

Уже испугались? Нет, это все не так уж сложно. В качестве варианта, я приведу вам в пример код моего дробовика из одного из моих модов (в поздней версии этого мода был заменен другим дробовиком):

 

DECORATE

ACTOR 12GaugeShotgun : weapon replaces Shotgun

{

weapon.selectionorder 44405

   Inventory.PickupMessage "Found 12 Gauge Pump Shotgun."

   Inventory.PickupSound "weapons/shotgun/pump"

   Weapon.AmmoType1 "Shellchamber" //патроны в дробовике

   Weapon.AmmoGive 0

   Weapon.AmmoUse 1

   Weapon.Ammotype2 "Shell1" //патроны запаса

   Weapon.ammogive2 8

obituary "%o ate %k's 12 gauge."

   +NOAUTOFIRE

   +AMMO_OPTIONAL

   States

   {

   Spawn:

      STGN A 1

      LOOP

   Ready:

      SHTN A 1 A_WeaponReady

      NULL A 0 A_jumpifinventory("reload",1,"reload") //это я объясню позже

      loop

   Reload: //и как я вызываю отдельный стейт для перезарядки – тоже позже

            NULL A 0 A_jumpifinventory("Shellchamber",8,"ready") //если патронов уже максимумв Ready

      SHTR A 3 //а вот это первый кадр анимации перезарядки, мне надо, чтобы он показывался только один раз, поэтому он здесь

            NULL A 0 A_jumpifinventory("Shell1",1,1) //проверяем, есть ли хотя бы 1 патрон в кармане       <-------------------------------------.

      goto ready                                                                                                                           //    |

      SHTR BC 3 //анимация поднесения руки с патроном к дробовику                                                                                |

      SHTR DE 2                                                                                                                            //    |

            NULL C 0 A_Takeinventory("Shell1",1) //взять 1 из кармана                                                                            |

            NULL C 0 A_giveinventory("Shellchamber",1) //положить патрон в дробовик                                                              |

            NULL C 0 A_playsound("weapons/shotgun/in") //со щелчком                                                                              |

      SHTR ED 2 //рука опускается вниз за экран, как бы за новым патроном                                                                        |

            NULL A 0 A_jumpifinventory("Shellchamber",2,6) //если у нас уже есть хотя бы 2 патрона в дробовике, прыгаем сюда ---,                |

      SHTR A 3 // а если мы вставили только первый патрон, то передергиваем затвор                                              |                |

            NULL A 0 A_Playsound("weapons/shotgun/pump") //со щелчком                                                           |                |

      SHTP ABA 4                                                                                                           //   |                |

            NULL A 0 A_jumpifinventory("Shellchamber",8,"ready")// если уже максимум патронов в Ready                         |                |

      goto reload+2 // <--------------------------------------------------------------------------------------------------------' прыгаем сюда –-'

   Deselect:

      SHTN A 1 A_Lower

      loop

   Select:

      SHTN A 1 A_Raise

      loop

   Fire:

      SHTF A 0 A_JumpIfNoAmmo("Reload") //если патронов нет – в Reload

      SHTF A 0 A_PlaySound("weapons/shotgun/fire")

      SHTF A 0 A_gunflash

      SHTF A 0 A_recoil(1) //отдача

      SHTF A 4 BRIGHT A_FireBullets(6,6,8,6,"BulletPuff")

      SHTF B 4 A_FireCustomMissile("SmokeSpawner",0,0,7,0) //а тут у меня спаунер дыма у ствола

      SHTP A 0 A_FireCustomMissile("ShotgunShellSpawner",0,0,14,0) //спаунер гильзы смещен вправо, потому что картинка ствола у меня тоже не в центре

      SHTP ABA 4

      SHTF B 3

      SHTN A 3

      Goto Ready

   Flash:

     TNT1 A 0

     TNT1 A 2 A_Light1

     TNT1 A 1 A_Light0

   }

}

 

 

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

Что происходит дальше? Вначале типичная проверка наличия патронов в кармане, доставание одного патрона из кармана и укладывание его в дробовик – отличие от других оружий в том, что здесь все это визуально отображается, ведь это дробовик. Затем, если в дробовике всего один патрон (т.е., если мы начали перезарядку с пустым дробовиком), проигрывается анимация передергивания затвора – патрон ведь нужно зарядить. Мы снова возвращаемся к началу стейта (точнее, к кадру с проверкой патронов в запасе, чтобы кадр SHTRA0 не повторялся) и вставляем следующий патрон. Дальше, если обнаруживается, что у нас уже есть хотя бы 2 патрона в дробовике, анимация передергивания затвора пропускается, и она будет пропускаться все следующие разы, пока мы будем вставлять патроны.

Кстати, кто-то мог заметить, что в Fire у меня не прописан звук "weapons/shotgun/pump", а ведь после каждого выстрела происходит передергивание затвора. Дело в том, что просто звук "weapons/shotgun/fire" у меня включает и звук выстрела, и звук перезарядки. Правда звук "weapons/shotgun/pump" немного отличается от того звука передергивания затвора, который звучит в "weapons/shotgun/fire", поэтому вам я советую такие звуки отделять друг от друга (это несложно сделать во многих звуковых программах, например Sound Forge).

 

Типичная ошибка, которую я сам очень часто допускал, когда впервые столкнулся с "проблемой дробовиков" - при вставке goto в конце стейта я перепрыгивал кадр с командой проверки наличия патронов в запасе, поэтому, даже если в кармане оставался 1 патрон, игрок заряжал все 8. Заметьте, что в данном примере я прыгаю именно со смещением +2, то есть как раз к нужному кадру. Поэтому, если в кармане осталось 3 патрона, игрок зарядит в дробовик ровно 3.

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

 

Команда A_Recoil вам уже знакома, я описывал ее в разделе про команды. Так я создал шотгану слабую отдачу.

 

Теперь поговорим о том, как я сделал перезарядку.

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

 

DECORATE

Actor Reload : Inventory

{

inventory.maxamount 1

}

 

 

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

 

KEYCONF

addkeysection "Name" name_keysection //вбейте сюда название своего мода или что-нибудь в этом роде

 

addmenukey "Reload" +z_reload //это название кнопки и привязка к ней команды                 

alias +z_reload "give Reload" //дает Reload

alias -z_reload "take Reload" //отнимает Reload

defaultbind r +z_reload //делаем стандартный бинд этой команды на 'r'

 

 

Вот и все. С помощью алиасов мы создали команду, дающую и отнимающую у нас управляющий итем. Через проверку инвентаря на наличие этого итема и происходит управление процессом перезарядки. Ссылку на него мы ставим в стейте Ready:

 

DECORATE

ACTOR 12GaugeShotgun : weapon replaces Shotgun

{

...

   Ready:

      SHTN A 1 A_WeaponReady

      NULL A 0 A_jumpifinventory("reload",1,"reload") //вот и ссылка к стейту reload

      loop

...

}

 

ЗАПОМНИТЕ! Никогда не ставьте ссылку на предмет в первый кадр, до A_WeaponReady. Почти в 100% случаев это влечет к вылету игры при попытке перезарядить оружие. Ставьте ссылку во второй кадр, сделав ему нулевую длину.

 

 

Упомянутый мной спаунер дыма у ствола устроен примерно так же, как спаунер гильзы. Только здесь проджектайл-спаунер спаунит не гильзу, а, собственно, дым. Дым – это простой проджектайл. Выглядит все это так:

 

DECORATE

Actor SmokeSpawner

{

    Radius 1

    Height 1

    Speed 20 //как и спаунер гильзы, пролетает немного вперед, перед тем как исчезнуть, чтобы оказаться у конца ствола

    PROJECTILE

    +NOCLIP

    States

    {

    Spawn:

        TNT1 A 1

        Goto Death

    Death:

        TNT1 A 0 A_CustomMissile("ShotSmoke",3,0) //спаунит дым чуть выше себя, без каких-либо угловых смещений

        Stop

    }

}

 

actor ShotSmoke

{

   Radius 1

   Height 1

   PROJECTILE

   +NOCLIP //чтобы не цеплялся за другие акторы и стены

   Speed 0

   RENDERSTYLE TRANSLUCENT //он должен быть прозрачным

   ALPHA 0.2 //довольно сильно прозрачным

   yscale 0.65

   xscale 0.3

   States

   {

   Spawn:

      SMKE ABCDEFGH 2 //анимация изображает дым, всплывающий вверх

      Stop

   Death:

      TNT1 A 0

      Stop

   }

}

 

Здесь я использовал не единый scale, а отдельно масштабирование по горизонтали и по вертикали. Это потому что спрайт дыма у меня был слишком круглым, а я хотел сделать его вытянутым. К тому же, он был слишком большим, поэтому я уменьшил его в обоих случаях.

Спрайты SMKEA(BCDEFGH)0 изображают сам дым. Причем каждый следующий спрайт имеет смещение по вертикали выше, чем предыдущий, чтобы создать эффект поднимающегося вверх и исчезающего дыма.

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

 

 

Теперь мы обсудим возможности использования стейта Hold. С его помощью можно создать такие оружие, как миниган – многоствольный пулемет, который перед началом стрельбы раскручивается, потом стреляет, а затем перестает крутиться. Сделать это легко. Мне впервые это понадобилось, когда я делал в Думе аналог FLK Cannon из Terminator: War of the Machines, но с тех пор я его усовершенствовал. Я покажу пример из усовершенствованного образца кода:

 

DECORATE

Actor FLK : weapon

{

      weapon.selectionorder 44400

      obituary "%o was ripped apart by %k's bulletstorm."

      weapon.kickback 80

      weapon.ammotype "FLKbullets"

      weapon.ammogive 300

      weapon.ammouse 1

      Inventory.PickupMessage "Obtained FLK Cannon!"

      Inventory.PickupSound "weapons/flk/ammo"

      height 20

      radius 50

      states

      {

      spawn:

            MINI G -1

            stop

      ready:

            FLKN A 1 A_weaponready

            loop

      select:

            FLKN a 1 A_raise

            loop

      deselect:

            FLKN a 1 A_lower

            loop

      Fire:

            NULL A 0 A_playsound ("weapons/flk/start")

            FLKS A 6 //начинается анимация раскручивания

            FLKS B 5

            FLKS C 4

            FLKS A 4

            FLKS B 3

            NULL A 0 A_refire //при наличии стейта Hold, команда A_ReFire отправляет нас в него

      hold:

            NULL A 0 A_PlaySoundEx("weapons/flk/spin","weapon") //звук вращения стволов на канале "weapon"

            NULL A 0 A_playsound ("weapons/flk/fire") //звук выстрела

            NULL A 0 A_recoil(0.3) //небольшая отдача при стрельбе

      NULL A 0 A_FireCustomMissile("FlkCasingSpawner",0,0,10,-10) //вылет пулеметной гильзы

            FLKF A 2 BRIGHT A_FireBullets(14,14,-1,7,0,1) //выстрел – с довольно большим разбросом

            NULL A 0 A_playsound ("weapons/flk/fire") //все повторяется, но стволы уже повернуты

            NULL A 0 A_recoil(0.3)

      NULL A 0 A_FireCustomMissile("FlkCasingSpawner",0,0,10,-10)

            FLKF B 2 BRIGHT A_FireBullets(14,14,-1,7,0,1)

            NULL A 0 A_playsound ("weapons/flk/fire")

            NULL A 0 A_recoil(0.3)

      NULL A 0 A_FireCustomMissile("FlkCasingSpawner",0,0,10,-10)

            FLKF C 2 BRIGHT A_FireBullets(14,14,-1,7,0,1)

            NULL A 0 A_refire //если мы продолжаем зажимать огонь, то прыжок к началу стейта Hold

            NULL A 0 A_playsound ("weapons/flk/stop") //если уже отпустили атаку, то идет анимация остановки стволов

            FLKS A 3 A_FireCustomMissile("SmokeSpawner",0,0,7,0) //и уже знакомый дым

            FLKS B 4

            FLKS C 5

            FLKS A 6

            FLKS B 7

            FLKN A 4

            goto ready

      }

}

 

 

Итак, есть кадры раскручивания и остановки вращения стволов, а также кадры вращения ствола во время стрельбы. Раскрутка и остановка – это одни и те же два кадра, проигрываемые с разной скоростью (не забывайте, что они при этом проигрываются в одном и том же порядке, ведь стволы вращаются всегда в одну сторону). И три кадра на стрельбу – они отличаются как минимум тем, что к ним пририсована вспышка выстрела. Этого вполне достаточно, чтобы создать реалистичное вращение. Весь принцип работы такого пулемета строится на свойстве A_ReFire – из Fire эта команда отправляет в Hold, а там она прокручивает весь Hold от первого кадра до того кадра, где написано A_ReFire, пока мы не отпустим кнопку атаки. Если мы ее отпускаем – показывается анимация остановки стволов.

У меня достаточно длинный звук вращения стволов (гудение), поэтому он указан один раз, в отличие от звуков стрельбы, которые указаны три раза. Если я попробую создать гудение через обычный A_PlaySound, то оно будет прервано звуком выстрела.

Разумеется, надо не забыть создать для пулемета патроны и т.п.

Некоторым очень не нравится отдача от оружия, но я посчитал, что маленький рекойл не помешает. Вы уж решайте сами.

 

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

 

 

Теперь поговорим о сущности пауэр-апа WeaponLevel2 и о создании супер-оружия.

Для начала, нам надо создать пауэр-ап. Все правила их создания вам уже знакомы, просто сделайте любой PowerupGiver с типом пауэр-апа WeaponLevel2.

 

Теперь в код вашего оружия добавьте проперти weapon.sisterweapon weaponname – она будет указывать на то, какое оружие будет замещать данное оружие при взятом пауэр-апе типа WeponLevel2. Например, сделаем брата вышеупомянутому пулемету:

 

DECORATE

Actor FLK : weapon

{

      weapon.sisterweapon SuperFLK //а вот мы указываем ссылку на супер-оружие

      weapon.selectionorder 44400

      obituary "%o was ripped apart by %k's bulletstorm."

      weapon.kickback 80

      weapon.ammotype "FLKbullets"

      weapon.ammogive 300

      weapon.ammouse 1

      Inventory.PickupMessage "Found Gatling Gun... Wooohaa!!"

      Inventory.PickupSound "weapons/flk/ammo"

      height 20

      radius 50

      states

      {

      spawn:

            MINI G -1

            stop

      ready:

            FLKN A 1 A_weaponready

            loop

      select:

            FLKN a 1 A_raise

            loop

      deselect:

            FLKN a 1 A_lower

            loop

      Fire:

            NULL A 0 A_playsound ("weapons/flk/start")

            FLKS A 6

            FLKS B 5

            FLKS C 4

            FLKS A 4

            FLKS B 3

            NULL A 0 A_refire

      hold:

            NULL A 0 A_playsound ("weapons/flk/fire")

            NULL A 0 A_recoil(0.3)

      NULL A 0 A_FireCustomMissile("FlkCasingSpawner",0,0,10,-10)

            FLKF A 2 BRIGHT A_FireBullets(14,14,-1,7,0,1)

            NULL A 0 A_playsound ("weapons/flk/fire")

            NULL A 0 A_recoil(0.3)

      NULL A 0 A_FireCustomMissile("FlkCasingSpawner",0,0,10,-10)

            FLKF B 2 BRIGHT A_FireBullets(14,14,-1,7,0,1)

            NULL A 0 A_playsound ("weapons/flk/fire")

            NULL A 0 A_recoil(0.3)

      NULL A 0 A_FireCustomMissile("FlkCasingSpawner",0,0,10,-10)

            FLKF C 2 BRIGHT A_FireBullets(14,14,-1,7,0,1)

            NULL A 0 A_refire

            NULL A 0 A_playsound ("weapons/flk/stop")

            FLKS A 3 A_FireCustomMissile("SmokeSpawner",0,0,7,0)

            FLKS B 4

            FLKS C 5

            FLKS A 6

            FLKS B 7

            FLKN A 4

            goto ready

      }

}

 

Actor SuperFLK : FLK //создаем усиленный вариант пулемета методом частичного заимствования

{

      weapon.sisterweapon FLK //не забудьте указать и обратную ссылку в усиленном варианте оружия

      states

      {

      hold:

            NULL A 0 A_playsound ("weapons/flk/fire")

      NULL A 0 A_FireCustomMissile("FlkCasingSpawner",0,0,10,-10)

            FLKF A 1 BRIGHT A_FireBullets(3,3,-1,15,0,1) //суперпулемет стреляет в 2 раза быстрее, с низким разбросом и повышенной мощностью

            NULL A 0 A_playsound ("weapons/flk/fire")

      NULL A 0 A_FireCustomMissile("FlkCasingSpawner",0,0,10,-10)

            FLKF B 1 BRIGHT A_FireBullets(3,3,-1,15,0,1)

            NULL A 0 A_playsound ("weapons/flk/fire")

      NULL A 0 A_FireCustomMissile("FlkCasingSpawner",0,0,10,-10)

            FLKF C 1 BRIGHT A_FireBullets(3,3,-1,15,0,1)

            NULL A 0 A_refire

            NULL A 0 A_playsound ("weapons/flk/stop")

            FLKS A 3 A_FireCustomMissile("SmokeSpawner",0,0,7,0)

            FLKS B 4

            FLKS C 5

            FLKS A 6

            FLKS B 7

            FLKN A 4

            goto ready

      }

}

 

 

 

Собственно, почти никаких комментариев не требуется. Пока будет длиться эффект подобранного пауэр-апа (вы ведь помните, как устанавливается длительность действия?), FLK заменится на SuperFLK, который стреляет в 2 раза быстрее, с низким разбросом и повышенной мощностью (или делает что-то еще – как захотите). При желании вообще можно сделать замену одного оружия другим.

 

Особенность создания двойных оружий в том, что оружие-брата (ну, или, выражаясь по-английский, оружие-сестру) не надо обозначать в KEYCONF – замена ствола на усиленный вариант и обратно будет происходить автоматически, так что вам не придется прописывать ничего лишнего. Разумеется, если усиленный вариант имеет какие-то кардинальные отличия от оригинала, вам надо не забыть их правильно реализовать (например, если он использует другие патроны, не забудьте их прописать).

 

 

Теперь осбудим уже упомянутое раньше оружие, собираемое из частей. Делается такое оружие очень просто.

Для начала надо решить, из скольки частей будет состоять собираемое оружие. Управляет этим числом, как ни странно, проперти health, которая в оружии, конечно, не имеет никакой связи ни с чьим здоровьем. Затем надо создать сами куски. Они будут PowerupGiver'ами:

 

DECORATE

Actor HuntingRifle : Weapon

{

health 3 //оружие будет состоять из трех частей

...

}

 

Actor RiflePart1 : PowerupGiver

{

inventory.pickupmessage "You found hunting rifle barrel!"

WeaponPiece.number 1 //первый кусок

WeaponPiece.Weapon "HuntingRifle" //отсылка к тому, от какого оружия этот кусок

states

{

Spawn:

RIFP A -1

stop

}

}

 

Actor RiflePart2 : PowerupGiver

{

inventory.pickupmessage "You found hunting rifle butt!"

WeaponPiece.number 2 //второй кусок

WeaponPiece.Weapon "HuntingRifle"

states

{

Spawn:

RIFP B -1

stop

}

}

 

Actor RiflePart3 : PowerupGiver

{

inventory.pickupmessage "You found hunting rifle trigger!"

WeaponPiece.number 3 //третий кусок

WeaponPiece.Weapon "HuntingRifle"

states

{

Spawn:

RIFP B -1

stop

}

}

 

 

 

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

 

 

 

FAQ

<<СОДЕРЖАНИЕ>>         

 

1) В ваде цветовая палитра спрайта выглядит не так, как я сделал ее в графическом редакторе. Как с этим справиться?

 

О: Я не знаю ничего лучше Photoshop, поэтому будем говорить о нем. Для начала через XWE вытащите из любого вада Дума любой спрайт (например, какой-нибудь стандартный). Затем откройте его в Фотошопе. Выберите ИзображениеàРежимà Индексированные цвета (Indexed Colors), если не выбирается, то выберите RGB, а потом снова индексированные цвета. В поле "Палитра" найдите выбор цветовой палитры и выберите последнюю строку ("Заказные"), а там выберите "Сохранить" – так вы сможете в файл сохранить думовскую цветовую палитру для Фотошопа.

Спрайты должны быть bmp  картинками Indexed Colors, в качестве палитры принудительно выставляйте сохраненную думовскую палитру (через "Загрузить").

Для редактирование картинку можно временно переводить в RGB, нередко нужно работать на нескольких слоях. Не забывайте, что цвет R(0)G(255)B(255) изображает прозрачные области спрайта – его можно взять инструментом "Пипетка" с какого-нибудь другого спрайта.

 

 

2) Как редактировать звуки, где их брать и в каких форматах вставлять?

 

О: Брать вы можете где угодно – распаковывать архивы других игр или других модов для Дума, например (если на то есть разрешение автора). Способов десятки. Для редактирования я предпочитаю Sound Forge 6.0. В вад надо вставлять либо звуки wav, либо (для лучшего результата) используйте формат ogg. Также можно вставлять midi или mp3 музыку.

 

 

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

 

О: Помним, что фактическое название спрайта – четыре буквы. Лучше всего первыми тремя буквами дать себе понять, к чему относится спрайт (например SMG – пистолет-пулемет (Sub-MachineGun), BMB – бомба (BoMB), XPL – взрыв (eXPLosion)), отталкиваясь от английских слов (как правило так проще), а последней буквой указать, к какому стейту относится спрайт (например SMGF – кадры стейта Fire, CHGS – кадры вращения стволов пулемета (от CHainGun и Spin)). Больше никаких особенных рекомендаций я дать не могу.

Брать спрайты можно много откуда. Пытаться перерисовывать чужие или оригинальные думовские, делать спрайты из скринов из других шутеров, делать спрайты на основе фотографий, или комбинировать все вышеупомянутое средствами Фотошопа. Или просто рисовать, если умеете.

 

 

4) Я вроде правильно выставил смещение в A_Jump(IfInventory,IfNoAmmo,…), а все равно в игре как-то не туда прыгает, что не так?

 

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

 

 

5) Я вставил в анимацию актора джамп-проверку A_JumpIfInventory, а он не работает, почему?

 

О: Запомните, что ЛЮБОЙ актор в игре может носить предметы – игрок, монстры, предметы. И, если вы вставляете в его анимацию проверку, идет проверка инвентаря самого актора. Исключение – если вы создаете CustomInventory, вы можете вставить проверку в его стейт Pickup, она будет проверять карманы игрока, т.к. действие Pickup связано с игроком.

 

 

6) Я играл со своим модом и обнаружил, что не могу пройти Doom 2 map 07, стены не опускаются, почему?

 

О: Это может быть в том случае, если вы сделали новых монстров, заменяющих Манкубусов, причем заменяете их не напрямую, а через спаунер, который может вместо Манкубуса поставить одного из нескольких врагов. В этом случае не работает интегрированный думовский скрипт map07special, который опускает стены, когда мертвы все манкубусы, и поднимает сектор, когда мертвы все арахнотроны. Аналогичная ситуация может быть, например, с Баронами на Doom 1 E1M8. Это можно решить следующим образом: спаунер новых врагов, который заменяет старых манкубусов, надо сделать монстром (дружественным, неподвижным, невидимым и неосязаемым), в анимации смерти которого должно быть прописано A_BossDeath, а в анимации смерти врагов, которых он спаунит, нужно прописать A_KillMaster – умирая, монстры будут убивать свой спаунер, и на смерть спаунера будет реагировать думовский скрипт.

 

 

7) В этом гайде описаны все лампы (Г)ЗДума и работа с ними?

 

О: Нет, я не затронул множество лампов, например MAPINFO, GLDEFS (GZDoom), ANIMDEFS и другие. Также я не описал многих команд. Все это – лишь основы, которые помогут начать. Дальше вам поможет ваш интерес, думерские форумы и ZDoom Wiki.

 

 

8) Гайд будет развиваться и дополняться?

 

О: Возможно, вполне возможно.

 

 

9) Я хочу прислать замечание, ты склонен их читать?

 

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

 

 

 

 

 

 

COPYRIGHT © 2008 Jekyll Grim Payne aka Ashigaru aka zer0

Contacts:

ashigaru-2403@yandex.ru

jekyllgrim@gmail.com

 

 

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

1) Вы не размещаете его на платном сайте;

2) Вы четко указываете имя автора.

Используются технологии uCoz