QuakeC:Плавающие объекты в Quake

Написано: воскресенье, 24 марта 2013 г. автор st1x51
0

Сложность: Средняя
Вы хотите, чтобы различные части тела, мертвые тела и рюкзачки в воде плавали? Тогда вы не должны пропустить этот урок, и даже с малыми знаниями QuakeC-программирования, вы сможете заставить другие вещи плавать.
Шаг 1 - Подготовка новой директории для мода.
Для начала, нам нужно скопировать неизмененные файлы QuakeC версии 1.06, а также скопировать файл floater.qc, содержание которого находится в самом конце этого урока, разумеется, вам нужно будет только создать файл с именем floater.qc и вставить в него текст, о котором чуть позже...
Итак, создайте в директории с Quake директорию, которую мы назовем FLOATER, и, чтобы Вы могли нормально делать мод, создайте внутри только что созданной директории еще одну директорию, которую назовем SRC.
Теперь скопируйте QuakeC файлы, а также файл floater.qc, в директорию SRC.
Шаг 2 - Добавление нового файла с исходного кодами
Так, как мы с вами внесли в наш мод новый файл, мы должны добавить его в список файлов для компиляции. Этим списком является файл progs.src, который сообщает компилятору, какие файлы с исходными кодами принадлежат к нашему моды.
Добавьте floater.qc после defs.qc в progs.src так, как показанно ниже:

defs.qc
floater.qc // наш новый файл
subs.qc
fight.qc

Шаг 3 - Изменение существующего исходного кода.
Изменения будут начинатся и заканчиватся коментариями // FLOATER.

Изменение файла client.qc. Сейчас мы с вами сделаем некоторое изменение в функции PlayerPreThink (ориентир на 914-ю строчку). После изменение начало функции должно выглядет так:

local float mspeed, aspeed;
local float r;

// FLOATER
// контроль "плавучести" над плавающими объектами
floaterPreThink();
// FLOATER

if (intermission_running)

Добавив floaterPreThink();, мы предоставляем Quake способность контролировать плавающие объекты (для краткости назовем их плавуны). Этот выполняется всегда на протяжении всей игры с некоторой задержкой (в один кадр), то есть если существуют какие-либо плавуны, то они сразу же становятся зависимыми от их текущих характеристик : они могут быть падающими, плавающими, погружающимися или могут просто лежать на поверхности.

Шаг 5 - Даем объектам способность плавать.
Погружающийся плавун зависит от скорости падения, но потом падает на какую-нибудь поверхность, где плавает, как поплавок, верх-вниз в течение 30 секунд и наконец падает на саму поверхность и теряет скорость.

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

Максумум 32 плавуна могут быть активированны в одно и тоже время (смотрите конфигурации в файле floater.qc) - в этом случае, когда какой-нибудь активированный плавун достигает поверхности (дна, пола, земли, и т.д.) нам нужно его деактивировать (т.е. сделать так, чтобы плавун использовал обычную quake-физику, например, как у объектов на поверхности).

Чтобы активировать у объекта способность плавания, мы должны вызвать свойство, которое называется floaterEnabl. Его мы опишем в файле floater.qc.

Сделайте изменения в файле player.qc (начиная, примерно, с 466-ой строчки) так, как показанно ниже:

new.origin = self.origin;
setmodel (new, gibname);

// FLOATER
// зададим объекту размеры по оси Z ... Тогда будет казаться, что он как бы "висит" в воде
setsize( new, '0 0 -4', '0 0 12' );
// FLOATER

new.velocity = VelocityForDamage (dm);
new.movetype = MOVETYPE_BOUNCE;


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

Следующие изменения в файле player.qc будут начинаются, с примерно 484-ой строчки.

new.frame = 0;
new.flags = 0;

// FLOATER
// указываем джибзам, чтобы они могли плавать
floaterEnable (new, 2);
// FLOATER

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

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

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

Центр объекта мы подсчитаем следующим образом:
Во-первых, как мы видим центр объекта в Quake?

Все модели в Quake окружает так называемая коробка, находящаяся в координатах 'X Y Z' (ниже центра модели, назовем их mins) и 'X Y Z' (выше центра модели, назовем их maxs), что дает частичный размер (maxs - mins) и центр (размер - maxs) коробке. Вы, наверное спросите, зачем нужна эта коробка? Что ж, она нужна для того, чтобы задать так называемый контур объекта, который будет принимать на себя все попадания по объекту, в том числе и попадания других объектов...

Следующая диаграмма покажет размещение головы (в смысле, воображаемой ;), которая имеет коробку с размерами '-16 -16 0', '16 16 56':


====== --- верхушка коробки (56)

------
[голова]
------ --- низ коробки (0), центр (0)
~~~~~~ --- поверхность воды

примечание : наша диаграма не имеет действительных размеров..

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

Только координата центра объекта по оси Z важна, когда мы делаем голову плавающей так, как надо. Подсчитав выражение ((56 - 0) - 56) мы найдем эту координату. В результате у нас получилось 0, т.е. координата находится фактически на дне коробки.

Чтобы взять плавающую как поплавок голову, мы передаем ей второй параметр свойства floaterEnable. Значение 5 (я использую 5, так как внешний размер (модель) головы как правило составляет 20% от всего размера коробки) сделает визуальный центр головы в воде так, как показано ниже:


====== --- верхушка коробки (56)

------
[голова] --- визуальный центр в воде (5)
------ --- низ коробки (0), центр (0)
~~~~~~ --- поверхность воды

Далее немного другой пример. Если тело умирает, то размеры коробки следующие: '-16 -16 -24', '16 16 32'.


====== --- верхушка коробки (56)
--- центр (24)
------
[тело] --- визуальный центр в воде (6)
------

====== --- низ коробки
~~~~~~ --- поверхность воды

В случае мертвого тела, нам нужно изменить центр на -18 (6-24), чтобы изменить визуальный центр для плавуна.

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

Шаг 6 - Доделываем наш мод.
1. Изменения в файле client.qc

Первые изменения начинаются примерно с 483-ей строки и заключаются в следующем:

void() PutClientInServer =
{
local entity spot;

// FLOATER
// делаем так, чтобы только мертвые игроки плавали как поплавки
floaterDisable (self);
// FLOATER

spot = SelectSpawnPoint ();

Следующее изменение начинается примерно с 791-ой строки.

if (self.movetype == MOVETYPE_NOCLIP)
return;

// FLOATER
// это будет проверкой для жизней при <0%, чтобы
// мертвый игрок не плавает так, как люди,
// а плавал так, как плавают трупы ;)
if( self.health <=0 ) return;
// FLOATER

if (self.waterlevel !=3)

2. Изменения в файле player.qc

Во-первых, перейдем на, примерно 504-ую строчку и сделаем следующие изменения:

self.flags = self.flags-(self.flags & FL_ONGROUND);
self.avelocity = crandom()* '0 600 0';

// FLOATER
// делаем голову плавающей
floaterEnable (self, 5);
// FLOATER

Далее перейдем на примерно 573-ю строчку:

self.angles_x = 0;
self.angles_z = 0;

// FLOATER
// теперь мы делаем тело плавающим
floaterEnable (self, -18);
// FLOATER

if (self.weapon == IT_AXE)

3. Изменения в файле combat.qc ведутся примерно с 78-ой строчки и заключаются в следующем:

if (self.flags & FL_MONSTER)
{
// FLOATER
// делаем тела мертвых монстров плавающими
floaterEnable (self, -18);
// FLOATER

killed_monsters = killed_monsters+1;
WriteByte (MSG_ALL, SVC_KILLEDMONSTER);
}

4. В файле world.qc изменения начинаются примерно с 390-ой строки:

setorigin (bodyque_head, ent.origin);
setsize (bodyque_head, ent.mins, ent.maxs);

// FLOATER
bodyque_head.sFloating = ent.sFloating;
bodyque_head.state = ent.state;
bodyque_head.speed = ent.speed;

bodyque_head.fOriginOffset = ent.fOriginOffset;

bodyque_head.ltime = ent.ltime;

if( ent.flags & FL_INWATER )
bodyque_head.flags = bodyque_head.flags | FL_INWATER;
// FLOATER

bodyque_head = bodyque_head.owner;

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

5. Изменения в файле items.qc протекают с 1380-ой строки:

item.nextthink = time+120; // убрать объект через 2 минуты
item.think = SUB_Remove;

// FLOATER
// делаем рюкзак, который выкидывается при смерти (назовем его бэкпэком) плавающим.
floaterEnable (item, 6);
// FLOATER

Шаг 7 - Вот и все.
Вот и все, что нам нужно было сделать, теперь компилируйте и запускайте Quake с нашими новыми модификациями.
Надеюсь вы будете в восторге от этого!

--------------------------------------------------------------------------------
P.S.: И еще одна последняя вещь. Вы наверное спросите где же все-таки взять содержимое для файла floater.qc? Отвечаем: оно находится ниже....

/*
==============================================================================
"floater.qc" разработал Alan Kivlin

12 мая 1997 года

Коментарии к модификации
-------------------------------

Данная модификация нужна для того, чтобы делать объекты плавающими в воде и на ее поверхности

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

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

Эта модификация зарегестрирована Alan Kivlin'ом в 1997 году.

Другие авторы МОГУТ использовать эту модификацию, как основу для других своих дополнений.
^^^^^^^^
ВНИМАНИЕ: Alan Kivlin (или как его еще называют Virtuoso) не несет ответственности за ущерб, психологические воздействия, потерю сна или усталость после использования данной модификации.
Если вы скопируете это на компакт-диск, то вы будете должны мне одну копию CD. Если вам это не нравится, то не копируйте это на CD!
Если вы возьмете мою работу и ни разу не упомяните обо мне, то ВЫ СГОРИТЕ В АДУ, так, как горят поросята Фроги в духовке.
Использование этой модификации в комерческих целях привет к тому, что к вам ночью завалятся дружки Сатаны и вам будет очень плохо, потому, что надо сначала переговорить со мной (Alan Kivlin), ok?

Спасибо Dave 'Zoid' Kirsch за его копирайт, который я поместил выше.
==============================================================================
*/

// вызов последнего кадра
float fFloaterLastFrame;

// делаем на объекте метку "floating", когда тот плавает
.string sFloating;

// изменения центра плавуна после проверки
.float fOriginOffset;

// флаги состояния плавуна
float FS_FALLING = 1; // падает
float FS_SURFACING = 2; // на поверхности
float FS_FLOATING = 3; // плавает
float FS_SINKING = 4; // опускается

// максимальное число одновременно плавающих объектов
float FLOATER_MAXIMUM = 32;

//----------------------------------------------------------------------------

// возвращаем значение TRUE (правда), если находится в воде, лаве или кислоте
float (entity ent) floaterInWater;

// делаем объект плавающим
void (entity ent, float offset) floaterEnable;

// делаем объект не плавающим
void (entity ent) floaterDisable;

// контролируем плавание всех плавунов
void () floaterPreThink;

//----------------------------------------------------------------------------

/*
==============================================================================
Значение "floaterInWater"
Возвращаем значение TRUE, если находится в воде, лаве или кислоте
==============================================================================
*/

float (entity ent) floaterInWater =
{
local vector where;
local float contents;

where = ent.origin;
where_z = where_z+ent.fOriginOffset;

contents = pointcontents (where);

if (contents >= -5 && contents <= -3) // в воде (-3), кислоте (-4) или лаве (-5) возвращаем TRUE.
return true;
return false;
};

/*
==============================================================================
Функция "floaterenable" вызывает у объекта способность плавать
==============================================================================
*/
void (entity ent, float offset) floaterenable=
{
local float floatercount;
local entity floater, oldest;
oldest = floater = find (world, sfloating, "floating");
while (floater)
{
floatercount = floatercount + 1;
if (floater.ltime <= oldest.ltime) oldest=floater;
floater = find (floater, sfloating, "floating");
}
if (floatercount == floater_maximum)
floaterdisable (oldest);
ent.sfloating = "floating";
ent.state = FS_FALLING;
ent.speed = 0; // сохраним изменения центра, использованного при проверке
ent.foriginoffset = offset; // "стартуем" время опускания плавуна
ent.ltime = time+30+random()*5;
if (floaterinwater(ent))
{
ent.movetype = MOVETYPE_TOSS;
ent.flags = ent.flags | fl_inwater; // ставим флаг "в воде"
}
else
{
ent.movetype = MOVETYPE_BOUNCE;
ent.flags = ent.flags-(ent.flags & fl_inwater); // убираем флаг "в воде"
}
};

/*
==============================================================================
Функция "floaterdisable" отнимает у объекта способность плавать
==============================================================================
*/
void( entity ent ) floaterdisable=
{
// прекращаем плавание
ent.sfloating = string_null;
};

/*
==============================================================================
Функция "floaterprethink" контролирует плавание всех плавунов
==============================================================================
*/
void() floaterprethink=
{
local entity ent;
if (ffloaterlastframe == framecount) return;
ffloaterlastframe = framecount;
ent = find (world, sfloating, "floating");
while (ent)
{
if ((ent.state == FS_FLOATING) && (ent.flags & fl_onground))
{
// если мы на земле, то мы должны уже упасть, это случается
// когда плавун на движущейся платформе, которая покинула воду
ent.state = FS_FALLING;
ent.speed = 0;
}
if (ent.state == FS_FLOATING)
{
if (ent.speed > 0) ent.velocity_z = ent.speed*(1+frametime*8); // плывем вверх
else ent.velocity_z = 0; // плывем вниз
}
else if (ent.state == FS_SURFACING)
// если у объекта стоит флаг "на поверхности", то
{
if (ent.velocity_z > 0) ent.velocity_z = ent.speed; // сохраняем скорость на поверхности
else if (floaterInWater(ent)) ent.state = FS_SINKING; // can't reach the surface so make it sink
}
else if (ent.state == FS_SINKING)
// иначе, если у объекта стоит флаг "погружающийся", то
if (ent.flags & FL_ONGROUND) floaterDisable(ent);
// если он уже на дне, то отбираем у него способность плавать
else ent.velocity_z = 0; // иначе, заставляем его погружаться медленно

if (floaterInWater(ent))
{
if (!(ent.flags & FL_INWATER))
{
ent.movetype = MOVETYPE_TOSS;

// ставим флаг "в воде"
ent.flags = ent.flags | FL_INWATER;

if (ent.state == FS_FLOATING) ent.speed = 72+random()*16;
// начинаем плыть вверх
else if (ent.state == FS_FALLING) sound (ent, CHAN_BODY, "player/h2ojump.wav", 1, ATTN_NORM);
// проигрываем звук вхождения в воду
}

if (ent.state == FS_FALLING)
{
if (!ent.speed)
{
if (ent.velocity_z < 0) // ставим максимальную скорость падения, до того как плавун достигнет поверхности
ent.speed = ent.velocity_z*8;
}
if ((ent.velocity_z <= ent.speed) || (ent.flags & fl_onground))
{
// start surfacing
ent.state = FS_SURFACING;
ent.speed = 128+random()*32;
ent.velocity_z = ent.speed;
ent.velocity_x = 0;
ent.velocity_y = 0;
}
}
}
else
{
if ((ent.flags & fl_inwater))
{
ent.movetype = MOVETYPE_BOUNCE;

//убираем флаг "в воде"
ent.flags = ent.flags-fl_inwater;
if (ent.state == FS_FLOATING) ent.speed = 0; // начинаем плавать вниз
else if (ent.state == FS_SINKING)
{
ent.state = FS_FALLING;
ent.speed = 0;
}
if (ent.state == FS_FALLING) sound (ent, chan_body, "player/h2ojump.wav" , 1, attn_norm); // play leave water sound
}
if (ent.state == FS_SURFACING)
{
// как только объект касается поверхности воды, то выталкиваем его наружу...
ent.velocity_z = ent.speed*1.5;
// ...и заставляем его плыть вниз
ent.state = FS_FLOATING;
ent.speed = 0;
}
}
if (ent.ltime <=time)
{
if (ent.flags & fl_inwater)
{
// если плавун достаточно опустился в воду, то ставим ему флаг "погружающийся"
ent.state = FS_SINKING;
ent.ltime = time+10+random()*5;
}
}
ent.watertype = 0;

// доругие физические движения не работают для плавуна
ent.flags = ent.flags-(ent.flags & fl_onground);
ent = find (ent, sfloating, "floating");
}

QuakeC:Персональный телепорт

Написано: автор st1x51
0

Сложность: Легкая
На это уроке, как вы уже догадались, мы создадим персональый телепортер. Все изменения проходят в файлах weapons.qc и defs.qc.
Шаг 1
Итак, для начала откроем файл weapon.qc и в самом верху, где написанно несколько нижеприведенных строчек, добавим наши четыре помеченные строчки.
/*
*/
void (entity targ, entity inflictor, entity attacker, float damage) T_Damage;
void () player_run;
void(entity bomb, entity attacker, float rad, entity ignore) T_RadiusDamage;
void(vector org, vector vel, float damage) SpawnBlood;
void() SuperDamageSound;
void() DropTeleport; //добавить
void() Teleport_to_drop; //добавить
void (vector org) spawn_tfog; //добавить
void (vector org, entity death_owner) spawn_tdeath; //добавить
Этим самым мы объявили четыре функции, которые мы создадим впоследствии.
Шаг 2
О'кей, теперь мы должны создать функции для телепорта. Итак, найдите функцию ImpulseCommands и перед ней добавте нижеследюющее:
/*
==========
Self Teleporter
==========
*/

void() DropTeleport = //эта функция кладет на месте игрока "точку для телепортации"(ТТ)
{
if(!self.tele_dropped)
{
self.teledrop_dest = spawn(); //создаем временный объект для ТТ
}
self.teledrop_dest.origin = self.origin; //запоминаем координаты ТТ
self.teledrop_dest.mangle = self.angles;
self.tele_dropped = 1;
dprint("Teleport destination dropped.");
};

void() Teleport_to_drop = //эта функция телепортирует игрока туда, где находится ТТ
{
local vector org;
if(!self.tele_dropped)
{
dprint("No destination dropped.");
return;
}
if(self.health <= 0)
{
return;
}
spawn_tfog (self.teledrop_dest.origin);
makevectors (self.teledrop_dest.mangle);
org=self.teledrop_dest.origin;
spawn_tfog (org);
spawn_tdeath (org,self);
setorigin (self,self.teledrop_dest.origin);
self.angles=self.teledrop_dest.mangle;
self.fixangle=1;
self.velocity=v_forward * 100;
self.teleport_time=time + 0.5;
self.flags=self.flags - self.flags & fl_onground;
remove(self.teledrop_dest); //удалите эту строчку, если вы хотите, чтобы ТТ оставался после телепортации.
};
Шаг 3
Теперь в функции Impulse Commands, что находится чуть ниже наших тлько что написанных функций, над строчкой self.impulse = 0; добавьте следующие две строчки:

if (self.impulse == 14) DropTeleport ();
if (self.impulse == 15) Teleport_to_drop ();
Шаг 4
И, наконец, последний шаг в нашем моде. Откройте файл defs.qc, и в самом конце этого файла добавте следующее:
.entity teledrop_dest;
.float tele_dropped;
(ОЧЕНЬ ВАЖНО, что все добавляемые строчки в файле defs.qc должны находится в самом конце этого файла!)
Шаг 5
Вот и все! Теперь откомпилируйте и запустите полученное. Не забудте присвоить клавишам новые impulse-команды (impulse 14 - скинуть ТТ, impulse 15 - телепортироваться)! Приятной вам игры...

QuakeC:Взрывпакет

Написано: автор st1x51
0

Сложность: Средняя
Шаг 1
Ок, в в этом уроке мы научимся создавать Взрывпакет. Звучит внушающе, не так ли? Нельзя не согласиться! Найдите функцию Impulse Commands в файле weapons.qc. Скопируйте код, который написан ниже и добавьте импульс-команду (impulse 20), которая будет активизировать DropDetpack:
/*
============
ImpulseCommands

============
*/
void() ImpulseCommands =
{
if (self.impulse >= 1 && self.impulse <= 8) w_changeweapon ();
if (self.impulse== 9) cheatcommand ();
if (self.impulse== 10) cycleweaponcommand ();
if (self.impulse== 11) serverflagscommand ();
if (self.impulse == 20) DropDetpack (); //активизировать функцию DropDetpack, когда мы введем в консоли "impulse 20".
if (self.impulse == 255) QuadCheat ();

self.impulse = 0;
};
Шаг 2
Так, теперь нам надо добавить функцию, которую мы назовем DropDetpack, в файле weapons.qc (давайте добавим ее чуть выше блока Player Weapon Use). Вот описание этой функции:
void() DropDetpack =
{
if (self.ammo_rockets >= 25)
//Смотрим, есть ли у игрока хотя бы 25 рокет. Если есть, то...
{
self.ammo_rockets = self.ammo_rockets - 25; //забираем у него 25 рокет,

local entity item; //создаем локальный объект (пусть будет "item"),

item = spawn(); //запускаем объект "item" в игру,
item.origin = self.origin - '0 0 24'; //кладем объект "item" ниже центра игрока на 24 еденицы (т.е. под ноги игрока),

item.velocity_z = 300; //подбрасываем объект "item" на 300 едениц вверх,
item.velocity_x = -100 + (random() * 200); //а по горизонтали объект "item" полетит
item.velocity_y = -100 + (random() * 200); //в каком-нибудь случайном направлении

item.flags = FL_ITEM;
item.solid = SOLID_TRIGGER; //SOLID_TRIGGER, означает, что если кто-то коснется объекта "item",
// то активизируется некоторый триггер(его мы пропишем чуть позже).
item.movetype = MOVETYPE_TOSS; //Тип движения... эээ... броском !!
setmodel (item, "progs/backpack.mdl"); //Присваеваем объекту "item" модель "backpack", находящуюся в директории "progs/",
setsize (item, '-16 -16 0', '16 16 56'); //Задаем габариты объекта "item" ('-x -y -z' , '+x +y +z' ),
item.owner = self; //говорим объекту "item", что его выпустили мы.
item.touch = DetpackTouch; //если наш взрывпакет упал на кого-нибудь, когда мы его выкинули,
// то активируем функцию DetpackTouch

item.nextthink = time + 120; //После 2 минут переходим к функции item.think
item.think = DetpackExplode; //которая "запускает" функцию DetpackExplode
}
else //Ну, а если нету 25 рокет, то...
{
sprint (self, "You haven't enough rockets, baby.\n"); //Выводим надпись "You haven't enough rockets, baby."
return; //и возвращаемся в игру
}
};
Шаг 3
О'кей. Теперь сделаем две функции : первая - DetpackTouch, которая прикажет Взрывпакету взорваться, если на нее наступит кто-нибудь (кроме нас самих, конечно) и вторая - DetpackExplode, которая... отлично сработает, если Взрывпакет попадает на кого нибудь во время его выброса. Т.е у нас есть две причины, чтобы взорвать Взрывпакета >:)

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

Вот код, что мы должны написать:

void () DetpackExplode =
{
T_RadiusDamage (self, self.owner, 1000, world); //Здесь мы задаем радиус поражения, у гранат он
//равен 120, но ведь мы хотим БОЛЬШОГО ВЗРЫВА,
//и поэтому ставим 1000 !!! ;-)

//Далее указываем, что :
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY); //а) объект темповый(временный),
WriteByte (MSG_BROADCAST, TE_EXPLOSION); //б) объект взрывчатый(хе-хе, а как же иначе-то?>:о).
WriteCoord (MSG_BROADCAST, self.origin_x); //создает X-координату для Backpack
WriteCoord (MSG_BROADCAST, self.origin_y); //создает Y-координату для Backpack
WriteCoord (MSG_BROADCAST, self.origin_z); //создает Z-координату для Backpack

BecomeExplosion (); //Производим ВЗРЫВ (БОЛЬШОЙ) !!!
};

void() DetpackTouch =
{
if (other == self.owner) //делаем так, чтобы detpack игнорировал нас, если он упадет на нас
{
return; //то есть возвращаемся в игру.
}
if (other.takedamage == DAMAGE_AIM) //Если игрок (кроме нас) или монстр, на которого упал наш detpack может быть поврежден,то
{
DetpackExplode(); //Производим ВЗРЫВ (см.предыдущую функцию) !!!
return; //и возвращаемся в игру.
}
if (self.velocity == '0 0 0') //если detpack уже не падает, то:
{
self.avelocity = '0 0 0'; //препятствуем "угловому движению"
}
};

Отлично... эти функции нужно добавлять ниже нашей функции DropDetpack.
Шаг 4
Последний шаг! Так, как наше дополнение использует новую модель (backpack.mdl), то нам нужно ее "подготовить". Итак, под подготовкой звуков в самом верху файла weapons.qc добавте самую нижнюю строчку примера:

precache_sound ("weapons/grenade.wav"); // звук выстрела из гранатомета
precache_sound ("weapons/bounce.wav"); // звук отскока гранаты от стены
precache_sound ("weapons/shotgn2.wav"); // звук выстрела из двустволки
precache_model ("progs/backpack.mdl"); // модель рюкзачка для DetPack

И кто тут сказал, что это трудно ?! Теперь компилируйте и посмотрите, что получилось. Во время игры наберите в консоли impulse 20 и посмотрите что получилось!!

QuakeC:Больше крови

Написано: автор st1x51
0

Сложность: Легкая
Шаг 1
Это дополнение дает больше крови в сетевой и одиночной играх . Все это делается очень легко в файле weapons.qc. Откройте этот файл для редактирования и опускайтесь вниз до нижеприведенных строчек.
/*
================
SpawnBlood
================
*/

void(vector org, vector vel, float damage) SpawnBlood =
{
particle (org, vel*0.1, 73, damage*2);
};

Шаг 2
Вставте три строчки, которые указаны ниже.
/*
================
SpawnBlood
================
*/

void(vector org, vector vel, float damage) SpawnBlood =
{
particle (org, vel*0.1, 79, damage*1);
particle (org, vel*0.25, 74, damage*5);
particle (org, vel*0.15, 69, damage*2);
};

Шаг 3
Вот и все! Согласитесь, что это было очень легко. Теперь компилируйте и запускайте полученное. Возмите в игре SuperShotgun (он сделает наш мод более эффективным :) и идите охотится на Grunt'ов! (Примечание: использование этого дополнения совместно с дополнением Больше мяса(урок 2) дает потрясающей эффект >:)

QuakeC:Магнум

Написано: автор st1x51
0

Сложность: Легкая
Шаг 1
Все это дополнение делается в файле weapons.qc. Когда мы его сделаем, у вас получится зверский Магнум! На этом уроке вы встретите несколько маленьких установок для оружия (таких, как на сколько градусов вверх идет отдача, конус разлета, и т.д.).. Но они очень легкие. К тому же, я помогу вам :) Теперь, найдите в файле weapons.qc текст, который похож на текст указанный в шаге 2.
Шаг 2
Итак, все, что вы должны сделать, это поменять некоторые числа, например так, как это показанно ниже.
(Внимание: не делайте Магнум слишком мощным, если вы расчитываете играть с ним по сети!)
/*
================
W_FireShotgun
================
*/
void() W_FireShotgun =
{
local vector dir;

sound (self, CHAN_WEAPON, "weapons/guncock.wav", 1, ATTN_NORM);
// воспроизвести звук выстрела из Shotgun

self.punchangle_x = -2; //угол обзора отклониться вверх на 2 единицы

self.currentammo = self.ammo_shells = self.ammo_shells - 1;
//здесь мы указываем, сколько патрон отнимется за один выстрел

dir = aim (self, 1000); //расстояние 100%-го попадания.

FireBullets (20, dir, '0.04 0.04 0');
//20 осколков в конусе разлета.
//0.04 - это высота и ширина конуса разлета (для Магнума лучше сделать 0.001).
};
Шаг 3
Вот и все! Компилируйте и запускайте полученное. Теперь ваш Shotgun будет сеять смерть вокруг.

QuakeC:Больше мяса

Написано: автор st1x51
0

Автор: Timm 'Mr?' Stokke
eMail: hestokke@online.no
Сложность: Легкая

Наиболее простой путь для увеличения количества мяса в сетевой игре
Шаг 1
Весь этот мод делается в файле player.qc, который нужно открыть для изменения. Когда вы все это сделаете, то у вас должно быть в два раза больше мыса в Сетевой игре. Все, что вам нужно будет делать, это копировать и вставлять некоторые строчки. Итак, переходим к самому основному, к шагу 2.
Шаг 2
Итак, скопируйте какую-либо строчку ThrowGib в функции GibPlayer, и вставте ее чуть ниже на одну строчку! А теперь сделаем мяса в два раза больше( т.е. так, как мы задумали). Посмотрите, как должно выглядеть оригинальное начало функции GibPlayer.
ThrowHead ("progs/h_player.mdl", self.health);
ThrowGib ("progs/gib1.mdl", self.health);
ThrowGib ("progs/gib2.mdl", self.health);
ThrowGib ("progs/gib3.mdl", self.health);
И примерно так оно может выглядеть после изменения.
ThrowHead ("progs/h_player.mdl", self.health);
ThrowGib ("progs/gib1.mdl", self.health);
ThrowGib ("progs/gib1.mdl", self.health);
ThrowGib ("progs/gib2.mdl", self.health);
ThrowGib ("progs/gib2.mdl", self.health);
ThrowGib ("progs/gib3.mdl", self.health);
ThrowGib ("progs/gib3.mdl", self.health);
Шаг 3
И это все! Скомпилируйте полученное, и запустите дополнение. Но не жалуйтесь, если оно не работает в одиночном режиме игры, так как оно работает только в сетевой игре (deathmatch и cooperative). Если вы хотите еще больше мяса, то копируйте и вставляйте строчки с ThrowGib по-больше, но только не перестарайтесь...

QuakeC:Сообщения о смерти

Написано: автор st1x51
0

Автор: Timm 'Mr?' Stokke
eMail: hestokke@online.no
Сложность: Легкая

Это очень легкий урок для тех, кому надоели старые сообщения о смерти в Quake. После прочтения этого урока, вы сможете изменять сообщения в сетевой (DM) и одиночной (single) играх. Только для начинающих!
Шаг 1
Все эти изменения производятся в файле под именем client.qc, который нужно открыть для редактирования. Что мы будем делать, чтобы поменять сообщения о смерти, которые возникают все время после того, как вас убивает какой-нибудь монстр? Опуститесь в самый низ открытого файла, где описаны все сообщения о смерти от монстров, от самоубийства и от других игроков при сетевой игре. Мы будем изменять сообщения, появляющиеся при смерти от монстра. Итак, найдите текст, похожий на текст, приведенный в шаге 2.
Шаг 2
А сейчас перейдем непосредственно к редактированию собщений о смерти. Я думаю вы сделаете их немного по своему, потому я не буду приводить детали
if (attacker.flags & FL_MONSTER)
{
if (attacker.classname == "monster_army")
bprint (" was shot by a Grunt\n"); //вывести в верхнем левом углу " was shot by a Grunt".
if (attacker.classname == "monster_demon1")
bprint (" was eviscerated by a Fiend\n");
if (attacker.classname == "monster_dog")
bprint (" was mauled by a Rottweiler\n");
if (attacker.classname == "monster_dragon")
bprint (" was fried by a Dragon\n");
if (attacker.classname == "monster_enforcer")
bprint (" was blasted by an Enforcer\n");
if (attacker.classname == "monster_fish")
bprint (" was fed to the Rotfish\n");
if (attacker.classname == "monster_hell_knight")
bprint (" was slain by a Death Knight\n");
if (attacker.classname == "monster_knight")
bprint (" was slashed by a Knight\n");
if (attacker.classname == "monster_ogre")
bprint (" was destroyed by an Ogre\n");
if (attacker.classname == "monster_oldone")
bprint (" became one with Shub-Niggurath\n");
if (attacker.classname == "monster_shalrath")
bprint (" was exploded by a Vore\n");
if (attacker.classname == "monster_shambler")
bprint (" was smashed by a Shambler\n");
if (attacker.classname == "monster_tarbaby")
bprint (" was slimed by a Spawn\n");
if (attacker.classname == "monster_vomit")
bprint (" was vomited on by a Vomitus\n");
if (attacker.classname == "monster_wizard")
bprint (" was scragged by a Scrag\n");
if (attacker.classname == "monster_zombie")
bprint (" joins the Zombies\n");
return;
Шаг 3
И это все! Откомпилируйте это, и запустите полученное. Теперь идите и... хммм.. и дайте себя убить!